1

I'm new to C#. I have a short form and a long form of a client code. The short form is some alpha characters and some numeric ones (ABC12), while the long form is always 15 characters long with the space between the alpha and the numeric parts padded out with zeros (ABC000000000012). I need to be able to convert from the short form to the long. The code below is how I've got it to work - is this the best way to do this?

public string ExpandCode(string s)
{
    // s = "ABC12"
    int i = 0;
    char c;
    bool foundDigit = false;
    string o = null;

    while (foundDigit == false)
    {
        c = Convert.ToChar(s.Substring(i, 1));
        if (Char.IsDigit(c))  
        {
            foundDigit = true;
            o = s.Substring(0, i) + new String('0', 15-s.Length) + s.Substring(i,s.Length-i); 
        }
        i += 1;
    }
    return (o); //o = "ABC000000000012"
}
Noctis
  • 11,507
  • 3
  • 43
  • 82

4 Answers4

5

Your code is basically correct, however it could be slow, since String.Substring(...) creates a new string every time called.

I also suggest that you use the built-in functions of the .NET api for accomplishing your tasks, which can make coding easier a lot:

private char[] numbers = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};

public string ExpandCode(string s)
{
    //Find the first numeric char.
    int index = s.IndexOfAny(numbers);
    //Insert zeros and return the result. 
    return s.Insert(index, new String('0', 15 - s.Length));
}
Emiswelt
  • 3,909
  • 1
  • 38
  • 56
  • That's great - thanks; I knew it could be more elegant. However when I tried your approach I got an error - "A const field of a reference type other than string can only be initialized with null". I found if I put the list of numbers directly into the indexOfAny function it worked fine. int index = s.IndexOfAny(new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }); Any reason why I shouldn't do this? – John Drinkwater Nov 12 '13 at 14:41
  • Thank you, I removed the "const". There is no real reason except I found it more easy to read. – Emiswelt Nov 13 '13 at 19:28
1

Take a look at this:

public string ExpandCode(string s)
{
    var builder = new StringBuilder(s);
    var index = Array.FindIndex(s.ToArray(), x => char.IsDigit(x));

    while (builder.Length < 15)
    {
        builder.Insert(index, '0');
    }

    return builder.ToString();
}

I assume that string are always letter -> digit (like "abc123" or "ab1234").

Alessandro D'Andria
  • 8,663
  • 2
  • 36
  • 32
1

The purer, the faster

public static string ExpandCode4(string s)
{
    char[] res = new char[15];
    int ind = 0;
    for (int i = 0; i < s.Length && s[i] >= 'A'; i++)
        res[ind++] = s[i];
    int tillDigit = ind;
    for (int i = 0; i < 15 - s.Length; i++)
        res[ind++] = '0';
    for (int i = 0; i < s.Length - tillDigit; i++)
        res[ind++] = s[tillDigit + i];
    return new string(res);
}

Benchmark for all the answers is as follows,

internal class Program
{
    private static void Main(string[] args)
    {
        var inputs = new List<string>();
        for (int i = 0; i < 10000000; i++)
        {
            inputs.Add("ABC1234");
        }

        var n1 = DateTime.Now;
        inputs.ForEach(i => ExpandCode1(i));
        var r1 = (DateTime.Now - n1).Ticks;

        var n2 = DateTime.Now;
        inputs.ForEach(i => ExpandCode2(i));
        var r2 = (DateTime.Now - n2).Ticks;

        var n3 = DateTime.Now;
        inputs.ForEach(i => ExpandCode3(i));
        var r3 = (DateTime.Now - n3).Ticks;

        var n4 = DateTime.Now;
        inputs.ForEach(i => ExpandCode4(i));
        var r4 = (DateTime.Now - n4).Ticks;

        var results = new List<Result>()
        {
            new Result() {Name = "1", Ticks = r1},
            new Result() {Name = "2", Ticks = r2},
            new Result() {Name = "3", Ticks = r3},
            new Result() {Name = "4", Ticks = r4}
        };
        results.OrderBy(r => r.Ticks).ToList().ForEach(Console.WriteLine);
        Console.ReadKey();
    }

    public static string ExpandCode4(string s)
    {
        char[] res = new char[15];
        int ind = 0;
        for (int i = 0; i < s.Length && s[i] >= 'A'; i++)
            res[ind++] = s[i];
        int tillDigit = ind;
        for (int i = 0; i < 15 - s.Length; i++)
            res[ind++] = '0';
        for (int i = 0; i < s.Length - tillDigit; i++)
            res[ind++] = s[tillDigit + i];
        return new string(res);
    }
    public static string ExpandCode1(string s)
    {
        char[] numbers = new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
        //Find the first numeric char.
        int index = s.IndexOfAny(numbers);
        //Insert zeros and return the result. 
        return s.Insert(index, new String('0', 15 - s.Length));
    }

    public static string ExpandCode2(string s)
    {
        var builder = new StringBuilder(s);
        var index = Array.FindIndex(s.ToArray(), x => char.IsDigit(x));

        while (builder.Length < 15)
        {
            builder.Insert(index, '0');
        }

        return builder.ToString();
    }

    public static string ExpandCode3(string s)
    {
        var match = Regex.Match(s, @"([^\d]+)(\d+)");
        var letters = match.Groups[1].Value;
        var numbers = int.Parse(match.Groups[2].Value);
        var formatString = "{0}{1:d" + (15 - letters.Length) + "}";
        var longForm = string.Format(formatString, letters, numbers);
        return longForm;
    }

}
public class Result
{
    public long Ticks { get; set; }
    public string Name { get; set; }
    public override string ToString()
    {
        return Name + " - " + Ticks;
    }
}
mehmet mecek
  • 2,615
  • 2
  • 21
  • 25
  • Sorry, but this approach is very slow. Strings are immutable in C#, and therefore, += creates a new string everytime, leading to a time complexity of O(n²) for your code. (It would suggest an implementation similar to this for C++, though) – Emiswelt Nov 12 '13 at 14:22
  • 1
    Yes you are right, I have fixed it and this is now 4 times faster than yours :) hihihi – mehmet mecek Nov 12 '13 at 14:51
  • +1 for benchmark. However, I wonder about the performance of the Regex version. – Emiswelt Nov 13 '13 at 19:30
0

You can use string.Format() to take care of the padding for you. I used regex (and not in a very robust manner) to parse out the letters and numbers but you may be able to do this more efficiently another way.

The key point is that we dynamically figure out how many zero's we need, and then use a format string of the form X:dY where X = format ordinal and Y = the number of zeros you wish to pad.

var match = Regex.Match("ABC12", @"([^\d]+)(\d+)");
var letters = match.Groups[1].Value;
var numbers = int.Parse(match.Groups[2].Value);
var formatString = "{0}{1:d" + (15 - letters.Length) + "}";
var longForm = string.Format(formatString, letters, numbers);
gooid
  • 841
  • 8
  • 18
  • This question has a more compact padding syntax: http://stackoverflow.com/questions/6842396/in-c-how-can-i-use-regex-replace-to-add-leading-zeroes-if-possible – Obsidian Phoenix Nov 12 '13 at 13:00
  • Good spot, it would probably be more efficient to do it like that. – gooid Nov 12 '13 at 13:02