Aman Mehrotra
Aman Mehrotra

Reputation: 413

Find a substring within a string in C#

I have a list of strings with the below format :

  • full1
  • full1inc1
  • full1inc2
  • full1inc3
  • full2
  • full2inc1
  • full2inc2
  • full3
  • ...
  • ....
  • full100inc100

Basically, the integer value after the "full" can be any number and the "inc" may or may not be there.

I have to pick an arbitrary string from this list , let's say the "full32" or the "full32inc1" and isolate the substring "full" along with the number that follows and put it in another string.

How could I do this? Should I use Regex or String Matching ?

Upvotes: 0

Views: 11621

Answers (4)

Bob.
Bob.

Reputation: 4002

The quickest way would be to brute force, but personally, the simpliest way I find would be to match a specific pattern, in this case full followed by a number.

string myString = "full1\nfull2s"; // Strings
List<string> listOfStrings = new List<string>(); // List of strings
listOfStrings.Add(myString);
Regex regex = new Regex(@"full[0-9]+"); // Pattern to look for
StringBuilder sb = new StringBuilder();
foreach(string str in listOfStrings) {
    foreach(Match match in regex.Matches(str)) { // Match patterns
        sb.AppendLine(match.Value);
    }
}

MessageBox.Show(sb.ToString());

Upvotes: 1

Tim Schmelter
Tim Schmelter

Reputation: 460288

Update according to this comment on my question "what's the desired result for full100inc100?"

The desired result would be full100

Then it's as easy as:

var isolatedList = listOfString
.Select(str => "full" + new string(
    str.Reverse()               // this is a backwards loop in Linq
       .TakeWhile(Char.IsDigit) // this takes chars until there is a non-digit char
       .Reverse()               // backwards loop to pick the digit-chars in the correct order
       .ToArray()               // needed for the string constructor
));

And if you want only the first number after "full":

var isolatedList = listOfString
    .Where(str => str.StartsWith("full"))
    .Select(str => "full" + new string(str.Substring("full".Length)
                                          .TakeWhile(Char.IsDigit)
                                          .ToArray()));

Demo

full1
full1
full2
full3
full2
full1
full2
full3
full100

Perhaps this:

var isolatedList = listOfString
    .Where(str => str.StartsWith("full"))
    .Select(str => "full" + string.Join("_", str.Substring("full".Length).Split(new[]{"inc"}, StringSplitOptions.None)))
    .ToList();

Change the separator in String.Join accordingly or just use the logic without linq:

"full" + 
string.Join("_", 
    str.Substring("full".Length).Split(new[]{"inc"}, StringSplitOptions.None))

Demo

full1
full1_1
full1_2
full1_3
full2
full2_1
full2_2
full3
full100_100

Upvotes: 1

DonBoitnott
DonBoitnott

Reputation: 11035

Based on your description, it seems like it might be possible that the strings are not guaranteed to be consistent. The following code will construct a List<String> of delimited (parsed) lines based on an incoming list of unknown content. It's a bit of a "brute force" method, but should allow for flexibility in the incoming list. Here is the code:

List<String> list = new List<String>();
list.Add("full1");
list.Add("full1inc1");
list.Add("full1inc2");
list.Add("full1inc3");
list.Add("full2");
list.Add("full2inc1");
list.Add("full2inc2");
list.Add("full3");
List<String> lines = new List<String>();
foreach (String str in list)
{
    String tmp = String.Empty;
    StringBuilder sb1 = new StringBuilder();
    StringBuilder sb2 = new StringBuilder();
    foreach (Char ch in str.ToCharArray())
    {
        if (Char.IsLetter(ch))
        {
            if (!String.IsNullOrEmpty(sb2.ToString()))
            {
              tmp += sb2.ToString() + ",";
                sb2 = new StringBuilder();
            }
            sb1.Append(ch);
        }
        else
        {
            if (!String.IsNullOrEmpty(sb1.ToString()))
            {
                tmp += sb1.ToString() + ",";
                sb1 = new StringBuilder();
            }
            sb2.Append(ch);
        }
    }
    if (!String.IsNullOrEmpty(sb1.ToString()))
        tmp += sb1.ToString() + ",";
    if (!String.IsNullOrEmpty(sb2.ToString()))
        tmp += sb2.ToString() + ",";
    lines.Add(tmp);
    for (Int32 i = 0; i < lines.Count; i++)
        lines[i] = lines[i].TrimEnd(',');
}

So, based on your example list, here's what you will get:

full1  -->  "full,1"

full1inc1  -->  "full,1,inc,1"

full1inc2  -->  "full,1,inc,2"

full1inc3  -->  "full,1,inc,3"

full2  -->  "full,2"

full2inc1  -->  "full,2,inc,1"

full2inc2  -->  "full,2,inc,2"

full3  -->  "full,3"

full100inc100  -->  "full,100,inc,100"

With this method, you will not need to presume that "full" is the leading string, or that it is followed by "inc" (or really anything at all).

Once you have the resulting delimited list, and because you know that the pattern is StringNumberStringNumber, you can then use whatever means you like to split those delimited lines into pieces and use them as you like.

Upvotes: 1

Caius Jard
Caius Jard

Reputation: 74710

In one line:

var matches = yourString.Split("\r\n".ToCharArray()).Where(s => s.StartsWith("full32"));

Requires LINQ, and is the conceptual equivalent of:

string[] lines = yourString.Split("\r\n".ToCharArray());
List<string> matches = new List<string>();
foreach(string s in lines)
  if(s.StartsWith("full32"))
    matches.Add(s);

Which may be more readable to you.

If you wanted to use Regex, something like this would capture them:

Regex r = new Regex("(?<f>full32(inc\d+)?)");
foreach(Match m in r.Matches(yourString))
  MessageBox.Show(m.Groups["f"].Value);

In all these examples, where you see "full32" in a string, that is where you should parameterize with a variable if you want to search for strings other than full32

Upvotes: 1

Related Questions