24

E.g., I would like to separate:

  • OS234 to OS and 234
  • AA4230 to AA and 4230

I have used following trivial solution, but I am quite sure that there should be a more efficient and robust solution .

private void demo()
    {   string cell="ABCD4321";
        int a = getIndexofNumber(cell);
        string Numberpart = cell.Substring(a, cell.Length - a);
        row = Convert.ToInt32(rowpart);
        string Stringpart = cell.Substring(0, a);
    }

private int getIndexofNumber(string cell)
        {
            int a = -1, indexofNum = 10000;
            a = cell.IndexOf("0"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
            a = cell.IndexOf("1"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
            a = cell.IndexOf("2"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
            a = cell.IndexOf("3"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
            a = cell.IndexOf("4"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
            a = cell.IndexOf("5"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
            a = cell.IndexOf("6"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
            a = cell.IndexOf("7"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
            a = cell.IndexOf("8"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }
            a = cell.IndexOf("9"); if (a > -1) { if (indexofNum > a) { indexofNum = a; } }

            if (indexofNum != 10000)
            { return indexofNum; }
            else
            { return 0; }


        }
SethO
  • 2,703
  • 5
  • 28
  • 38
Thunder
  • 10,366
  • 25
  • 84
  • 114

10 Answers10

35

Regular Expressions are best suited for this kind of work:

using System.Text.RegularExpressions;

Regex re = new Regex(@"([a-zA-Z]+)(\d+)");
Match result = re.Match(input);

string alphaPart = result.Groups[1].Value;
string numberPart = result.Groups[2].Value;
JHowIX
  • 1,683
  • 1
  • 20
  • 38
x0n
  • 51,312
  • 7
  • 89
  • 111
  • 3
    Might want to add two lines to this answer: string alphaPart = result.Groups[1]; string numberPart = result.Groups[2]; – Brett Daniel Dec 28 '09 at 05:16
  • Following seems to be more complete solution: Regex re = new Regex(@"([a-zA-Z]+)(\d+)"); Match result = re.Match("as23"); string alphaPart = result.Groups[1].ToString (); string numberPart = result.Groups[2].ToString(); – Thunder Dec 28 '09 at 05:17
  • 2
    Isn't it `result.Groups[1].Value`? – Dmitri Nesteruk Dec 28 '09 at 06:02
  • result.Groups[2].ToString(); Worked for me Further we should add Imports Systems.Text.Regularexpression @ top. – Thunder Dec 28 '09 at 08:41
  • "GJ10HE1895" is my string any i want it like GJ,10,HE, 1895 seperately in different group can you suggest solution. @x0n – Ankit Mori Mar 13 '19 at 13:22
13

Use Linq to do this

string str = "OS234";

var digits = from c in str
             select c
             where Char.IsDigit(c);

var alphas = from c in str
             select c
             where !Char.IsDigit(c);
this. __curious_geek
  • 42,787
  • 22
  • 113
  • 137
6

Everyone and their mother will give you a solution using regex, so here's one that is not:

 // s is string of form ([A-Za-z])*([0-9])* ; char added
 int index = s.IndexOfAny(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
 string chars = s.Substring(0, index);
 int num = Int32.Parse(s.Substring(index));
UNeverNo
  • 549
  • 3
  • 8
  • 29
jason
  • 236,483
  • 35
  • 423
  • 525
6

I really like jason's answer. Lets improve it a bit. We dont need regex here. My solution handle input like "H1N1":

public static IEnumerable<string> SplitAlpha(string input)
{
    var words = new List<string> { string.Empty };
    for (var i = 0; i < input.Length; i++)
    {
        words[words.Count-1] += input[i];
        if (i + 1 < input.Length && char.IsLetter(input[i]) != char.IsLetter(input[i + 1]))
        {
            words.Add(string.Empty);
        }
    }
    return words;
}

This solution is linear O(n).

output

"H1N1" -> ["H", "1", "N", "1"]
"H" -> ["H"]
"GH1N12" -> ["GH", "1", "N", "12"]
"OS234" -> ["OS", "234"]

Same solution with a StringBuilder

public static IEnumerable<string> SplitAlpha(string input)
{
    var words = new List<StringBuilder>{new StringBuilder()};
    for (var i = 0; i < input.Length; i++)
    {
        words[words.Count - 1].Append(input[i]);
        if (i + 1 < input.Length && char.IsLetter(input[i]) != char.IsLetter(input[i + 1]))
        {
            words.Add(new StringBuilder());
        }
    }

    return words.Select(x => x.ToString());
}

Try it Online!

aloisdg
  • 22,270
  • 6
  • 85
  • 105
4

If you want resolve more occurrences of char followed by number or vice versa you can use

private string SplitCharsAndNums(string text)
{
    var sb = new StringBuilder();
    for (var i = 0; i < text.Length - 1; i++)
    {
        if ((char.IsLetter(text[i]) && char.IsDigit(text[i+1])) ||
            (char.IsDigit(text[i]) && char.IsLetter(text[i+1])))
        {
            sb.Append(text[i]);
            sb.Append(" ");
        }
        else
        {
            sb.Append(text[i]);
        }
    }

    sb.Append(text[text.Length-1]);

    return sb.ToString();
}

And then

var text = SplitCharsAndNums("asd1 asas4gr5 6ssfd");
var tokens = text.Split(' ');
y0j0
  • 3,369
  • 5
  • 31
  • 52
  • 1
    Works well until you meet a non-letter or numeric, such as a . in a file name. Change char.IsLetter to !char.IsNumeric – Tod Jan 18 '19 at 10:28
1

Are you doing this for sorting purposes? If so, keep in mind that Regex can kill performance for large lists. I frequently use an AlphanumComparer that's a general solution to this problem (can handle any sequence of letters and numbers in any order). I believe that I adapted it from this page.

Even if you're not sorting on it, using the character-by-character approach (if you have variable lengths) or simple substring/parse (if they're fixed) will be a lot more efficient and easier to test than a Regex.

Aaronaught
  • 120,909
  • 25
  • 266
  • 342
1

.NET 2.0 compatible, without regex

public class Result
{
    private string _StringPart;
    public string StringPart
    {
        get { return _StringPart; }
    }

    private int _IntPart;
    public int IntPart
    {
        get { return _IntPart; }
    }

    public Result(string stringPart, int intPart)
    {
        _StringPart = stringPart;
        _IntPart = intPart;
    }
}

class Program
{
    public static Result GetResult(string source)
    {
        string stringPart = String.Empty;
        int intPart;
        var buffer = new StringBuilder();
        foreach (char c in source)
        {
            if (Char.IsDigit(c))
            {
               if (stringPart == String.Empty)
               {
                    stringPart = buffer.ToString();
                    buffer.Remove(0, buffer.Length);
                }
            }

            buffer.Append(c);
        }

        if (!int.TryParse(buffer.ToString(), out intPart))
        {
            return null;
        }

        return new Result(stringPart, intPart); 
    }

    static void Main(string[] args)
    {
        Result result = GetResult("OS234");
        Console.WriteLine("String part: {0} int part: {1}", result.StringPart, result.IntPart);
        result = GetResult("AA4230 ");
        Console.WriteLine("String part: {0} int part: {1}", result.StringPart, result.IntPart);
        result = GetResult("ABCD4321");
        Console.WriteLine("String part: {0} int part: {1}", result.StringPart, result.IntPart);
        Console.ReadKey();
    }
}
bniwredyc
  • 8,649
  • 1
  • 39
  • 52
1

I have used bniwredyc's answer to get Improved version of my routine:

    private void demo()
        {
            string cell = "ABCD4321";
            int row, a = getIndexofNumber(cell);
            string Numberpart = cell.Substring(a, cell.Length - a);
            row = Convert.ToInt32(Numberpart);
            string Stringpart = cell.Substring(0, a);
        }

        private int getIndexofNumber(string cell)
        {
            int indexofNum=-1;
            foreach (char c in cell)
            {
                indexofNum++;
                if (Char.IsDigit(c))
                {
                    return indexofNum;
                }
             }
            return indexofNum;
        }
Thunder
  • 10,366
  • 25
  • 84
  • 114
-2

Just use the substring function and set position inside the bracket.

 String id = "DON123";
 System.out.println("Id nubmer is : "+id.substring(3,6));

Answer:

 Id number is: 123
-3

use Split to seprate string from sting that use tab \t and space

string s = "sometext\tsometext\tsometext";
string[] split = s.Split('\t');

now you have an array of string that you want too easy

Slava Vedenin
  • 58,326
  • 13
  • 40
  • 59
  • this answer is irrelevant to the question, it's not about separating number from letters at all. also your "too easy" in the end sounds spiteful, author wouldn't ask question if that was as easy as this answer states – Andrew Feb 19 '23 at 16:23