tags:

views:

30

answers:

3

Quick Question for anyone who is good at Regexes.

I am trying to obtain the 'value' part of this syntax:

[name:value]

I got as far as:

\[name:(\w*)]

Which matches the entire thing. Does anyone know the changes I would need to make to select only value? (Bearing in mind that syntax will also be surrounded by other text.

Thanks Damien

+1  A: 

The regex will always match the entire string, but you can access the matching portion with the Groups member collection:

public static void Main()
{
   string pattern = @"(\d{3})-(\d{3}-\d{4})";
   string input = "212-555-6666 906-932-1111 415-222-3333 425-888-9999";
   MatchCollection matches = Regex.Matches(input, pattern);

   foreach (Match match in matches)
   {
      Console.WriteLine("Area Code:        {0}", match.Groups[1].Value);
      Console.WriteLine("Telephone number: {0}", match.Groups[2].Value);
      Console.WriteLine();
   }
   Console.WriteLine();
}

(this example is taken from the page referenced)

Michael Bray
How does it know the difference between groups?
Damien
Good, but Match.Result is simpler.
Daniel Straight
A: 

You could use lookaround to only match value.

Search for (?<=\[name:)\w+(?=])

This means: Match one or more alphanumeric characters, if and only if they are preceded by [name and followed by ]. Note that it's not necessary to escape the ].

Tim Pietzcker
This is not necessary as .NET will give you access to parenthesized parts of regexes.
Daniel Straight
I know that it's not necessary, but it's what the OP wanted - an alternative solution by Michael Bray was already there, and now there is another one by yourself.
Tim Pietzcker
+1  A: 

The Match.Result method is what you want. See API documentation and documentation on Regex substitution patterns.

Use Regex.Match(input, pattern).Result("$1") to get what you want.

To give a complete example, here is the unit test I used to confirm that this solution would work:

[Test]
public void TestRegexMatchResult() {
    var input = "Other text [name:value] and other text";
    var pattern = @"\[name:(\w+)\]";

    Assert.AreEqual("value", Regex.Match(input, pattern).Result("$1"));
}

The test passes.

You can also go through a string and extract multiple occurrences of the pattern:

[Test]
public void TestRegexMatchesResult() {
    var input = "Some [name:value] pairs [name:something] here.";
    var pattern = @"\[name:(\w+)\]";

    var results = Regex.Matches(input, pattern).OfType<Match>()
        .Select(match => match.Result("$1"));

    Assert.AreEqual(2, results.Count());

    Assert.AreEqual("value", results.ElementAt(0));
    Assert.AreEqual("something", results.ElementAt(1));
}

Finally, if you need to abstract this to something other than "name", you can do this:

[Test]
public void TestMatchingNameAndValue() {
    var input = "[key:value] [another_key:some_other_value]";
    var pattern = @"\[(\w+):(\w+)\]";

    var results = Regex.Matches(input, pattern).OfType<Match>()
        .Select(match => new KeyValuePair<string, string>(
            match.Result("$1"),
            match.Result("$2")));

    Assert.AreEqual(2, results.Count());

    Assert.AreEqual("key", results.ElementAt(0).Key);
    Assert.AreEqual("value", results.ElementAt(0).Value);
    Assert.AreEqual("another_key", results.ElementAt(1).Key);
    Assert.AreEqual("some_other_value", results.ElementAt(1).Value);
}
Daniel Straight
Excellent. Thank You. Could you explain how the $1 thing works?
Damien
$number just gets the corresponding parenthesized group. You have one set of parenthesis, so one group, and that group is number 1, so `$1` gets that group.
Daniel Straight
Very useful! Thanks once again!
Damien