views:

1117

answers:

5

What's the easiest way to parse a string and extract a number and a letter? I have string that can be in the following format (number|letter or letter|number), i.e "10A", "B5", "C10", "1G", etc.

I need to extract the 2 parts, i.e. "10A" -> "10" and "A".

Update: Thanks to everyone for all the excellent answers

+7  A: 

Easiest way is probably to use regular expressions.

((?<number>\d+)(?<letter>[a-zA-Z])|(?<letter>[a-zA-Z])(?<number>\d+))

You can then match it with your string and extract the value from the groups.

Match match = regex.Match("10A");
string letter = match.Groups["letter"].Value;
int number = int.Parse(match.Groups["number"].Value);
Samuel
+5  A: 

The easiest and fastest is to use simple string operations. Use the IsDigit method to check where the letter is, and parse the rest of the string to a number:

char letter = str[0];
int index = 1;
if (Char.IsDigit(letter)) {
   letter = str[str.Length - 1];
   index = 0;
}
int number = int.Parse(str.Substring(index, str.Length - 1));
Guffa
The order of the number/letter is not guaranteed.
Samuel
@Samuel: Yes, it is. The question specifies that it's either number|letter or letter|number. If you don't think that the code handles both, check the code again.
Guffa
@Guffa: If the letter is at the end, you will pass it into int.Parse.
Samuel
@Samuel: No, I won't. Check the code again.
Guffa
Great answer. I love the way you use the information that there is only one letter and it has to be either the first or the last character.
Michał Piaskowski
@Guffa: Sorry, my mistake. I had a little brain fart. Substring's second argument is the length not position.
Samuel
+2  A: 

Just to be different:

string number = input.Trim("ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray());
string letter = input.Trim("0123456789".ToCharArray());
Daniel LeCheminant
+1  A: 

Here is how I would approach this. You can step through this and put gc1["letter"], gc1["number"], gc2["letter"], and gc2["number"] in the watch window to see that it worked (step just past the last line of code here, of course).

The regular epxression will take either pattern requiring one or more letter and number in each case.

        Regex pattern = new Regex("^(?<letter>[a-zA-Z]+)(?<number>[0-9]+)|(?<number>[0-9]+)(?<letter>[a-zA-Z]+)$");
        string s1 = "12A";
        string s2 = "B45";
        Match m1 = pattern.Match(s1);
        Match m2 = pattern.Match(s2);
        GroupCollection gc1 = m1.Groups;
        GroupCollection gc2 = m2.Groups;
Rich
If you're going to pretty much copy my answer, at least get the regex right. It should be [a-zA-Z], with no plus.
Samuel
+3  A: 
char letter = str.Single(c => char.IsLetter(c));
int num = int.Parse(new string(str.Where(c => char.IsDigit(c)).ToArray()));

This solution is not terribly strict (it would allow things like "5A2" and return 'A' and 52) but it may be fine for your purposes.

mquander