views:

101

answers:

6

Hello. I have a string with 16 alphanumeric characters, e.g. F4194E7CC775F003. I'd like to format it as F419-4E7C-C775-F003.

I tried using

string.Format("{0:####-####-####-####}","F4194E7CC775F003");

but this doesn't work since it's not a numeric value.

So I came up with the following:

public class DashFormatter : IFormatProvider, ICustomFormatter
{
  public object GetFormat(Type formatType)
  {
    return this;
  }

  public string Format(string format, object arg, IFormatProvider formatProvider)
  {
    char[] chars = arg.ToString().ToCharArray();
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < chars.Length; i++)
    {
      if (i > 0 && i % 4 == 0)
      {
        sb.Append('-');
      }

      sb.Append(chars[i]);
    }

    return sb.ToString();
  }
}

and by using

string.Format(new DashFormatter(), "{0}", "F4194E7CC775F003");

I was able to solve the problem, however I was hoping there is a better/simpler way to do it? Perhaps some LINQ magic?

Thanks.

A: 
 char[] chars = "F4194E7CC775F003".ToCharArray();
            var str = string.Format("{0}-{1}-{2}-{3}"
                                  , new string(chars.Take(4).ToArray())
                                  , new string(chars.Skip(4).Take(4).ToArray())
                                  , new string(chars.Skip(8).Take(4).ToArray())
                                  , new string(chars.Skip(12).Take(4).ToArray())  
                );
Amgad Fahmi
Quite ugly, but LINQ indeed ;-)
Steven
I'm sorry, but this looks horribly inefficient, like Schlemiel the Painter's algorithm (http://www.joelonsoftware.com/articles/fog0000000319.html)
Zer0
loool , yeah it's ugly :)
Amgad Fahmi
it's 8 times slower than Carra's solution
AdamRalph
+8  A: 

You can do it in one line without Linq:

        StringBuilder  splitMe = new StringBuilder("F4194E7CC775F003");
        string joined = splitMe.Insert(12, "-").Insert(8, "-").Insert(4, "-").ToString();
Carra
+1 better than mine, and better than any of the LINQ solutions
AdamRalph
Added stringbuilder.
Carra
I was wondering which was more efficient, your or mine, so I did a quick benchmark - yours is twice as fast ;-)
AdamRalph
That's two lines! ;-)
Paul Manzotti
Nice, but I would argue that's two lines.
DanM
it can be rewritten in one line as string joined = new StringBuilder("F4194E7CC775F003").Insert(12, "-").Insert(8, "-").Insert(4, "-").ToString();
AdamRalph
@Adam: How does the performance compare to the OP's solution?
Paul Manzotti
@Paul - good question. Carra's solution is 3 times faster
AdamRalph
I'll accept this answer, but I like hmemcpy's solution better. Thanks! :)
Zer0
+1  A: 

If you want it linq:

var formatted = string.Join("-", Enumerable.Range(0,4).Select(i=>s.Substring(i*4,4)).ToArray());

And if you want it efficient:

var sb = new StringBuilder(19);
sb.Append(s,0,4);
for(var i = 1; i < 4; i++ )
{
 sb.Append('-');
 sb.Append(s,i*4, 4);
}
return sb.ToString();

I did not benchmark this one, but i think it would be faster then StringBuilder.Insert because it does not move the rest of string many times, it just writes 4 chars. Also it would not reallocate the underlying string, because it's preallocated to 19 chars at the beginning.

George Polevoy
so damn coool :)
Amgad Fahmi
but i think u r missing casting var s= string.Join("-", Enumerable.Range(0, 4).Select(i => "F4194E7CC775F003".Substring(i * 4, 4)).ToArray());
Amgad Fahmi
it may look like cool code, but it's very inefficient. I just did some benchmarks and Carra's solution is 3 times faster
AdamRalph
Yeah, sorry, i din't try to compile, now it's corrected. I'm actually using my own extension for string, and it looks like foo.Select(bar).JoinWithSeparator('-')
George Polevoy
Yes, i agree it's inefficient. The most efficient one would be working with arrays of chars in C++ style, not a string builder.
George Polevoy
@George - "working with arrays of chars in C++ style" - I believe this is exactly what StringBuilder does internally
AdamRalph
@AdamRalph what tools you are using to get these statistics and comparison between different algorithms ????
Amgad Fahmi
@TiTaN - Visual Studio and C# ;-) I just wrote a quick unit test which runs each method 100,000 times and compares the time taken
AdamRalph
A: 

Simplest solution I can think of is

        var text = "F4194E7CC775F003";
        var formattedText = string.Format(
            "{0}-{1}-{2}-{3}",
            text.Substring(0, 4),
            text.Substring(4, 4),
            text.Substring(8, 4),
            text.Substring(12, 4));
AdamRalph
same as TiTaN, less ugly ;)
PoweRoy
Carra's solution is twice as fast
AdamRalph
+1  A: 

You could do it with a regular expression, though I don't know what the performance of this would be compared to the other methods.

string formattedString = Regex.Replace(yourString, "(\\S{4})\\B", "$1-");

You could put this in an extension method for string too, if you want to do:

yourString.ToDashedFormat();
Paul Manzotti
sorry but this is 25 times slower than Carra's solution
AdamRalph
Ouch! Well, I did say that I didn't know what the performance would be like!
Paul Manzotti
+2  A: 

Based on Carra's answer I made this little utility method:

private static string ToDelimitedString(string input, int position, string delimiter)
{
  StringBuilder sb = new StringBuilder(input);

  int x = input.Length / position;

  while (--x > 0)
  {
    sb = sb.Insert(x * position, delimiter);
  }

  return sb.ToString();
}

You can use it like this:

string result = ToDelimitedString("F4194E7CC775F003", 4, "-");

And a test case:

[Test]
public void ReturnsDelimitedString()
{
  string input = "F4194E7CC775F003";

  string actual = ToDelimitedString(input, 4, "-");

  Assert.AreEqual("F419-4E7C-C775-F003", actual);
}
hmemcpy