How do I access named capturing groups in a .NET Regex?

C#.NetRegex

C# Problem Overview


I'm having a hard time finding a good resource that explains how to use Named Capturing Groups in C#. This is the code that I have so far:

string page = Encoding.ASCII.GetString(bytePage);
Regex qariRegex = new Regex("<td><a href=\"(?<link>.*?)\">(?<name>.*?)</a></td>");
MatchCollection mc = qariRegex.Matches(page);
CaptureCollection cc = mc[0].Captures;
MessageBox.Show(cc[0].ToString());

However this always just shows the full line:

<td><a href="/path/to/file">Name of File</a></td> 

I have experimented with several other "methods" that I've found on various websites but I keep getting the same result.

How can I access the named capturing groups that are specified in my regex?

C# Solutions


Solution 1 - C#

Use the group collection of the Match object, indexing it with the capturing group name, e.g.

foreach (Match m in mc){
    MessageBox.Show(m.Groups["link"].Value);
}

Solution 2 - C#

You specify the named capture group string by passing it to the indexer of the Groups property of a resulting Match object.

Here is a small example:

using System;
using System.Text.RegularExpressions;

class Program
{
	static void Main()
	{
		String sample = "hello-world-";
		Regex regex = new Regex("-(?<test>[^-]*)-");

		Match match = regex.Match(sample);

		if (match.Success)
		{
			Console.WriteLine(match.Groups["test"].Value);
		}
	}
}

Solution 3 - C#

The following code sample, will match the pattern even in case of space characters in between. i.e. :

<td><a href='/path/to/file'>Name of File</a></td>

as well as:

<td> <a      href='/path/to/file' >Name of File</a>  </td>

Method returns true or false, depending on whether the input htmlTd string matches the pattern or no. If it matches, the out params contain the link and name respectively.

/// <summary>
/// Assigns proper values to link and name, if the htmlId matches the pattern
/// </summary>
/// <returns>true if success, false otherwise</returns>
public static bool TryGetHrefDetails(string htmlTd, out string link, out string name)
{
    link = null;
    name = null;

    string pattern = "<td>\\s*<a\\s*href\\s*=\\s*(?:\"(?<link>[^\"]*)\"|(?<link>\\S+))\\s*>(?<name>.*)\\s*</a>\\s*</td>";

    if (Regex.IsMatch(htmlTd, pattern))
    {
        Regex r = new Regex(pattern,  RegexOptions.IgnoreCase | RegexOptions.Compiled);
        link = r.Match(htmlTd).Result("${link}");
        name = r.Match(htmlTd).Result("${name}");
        return true;
    }
    else
        return false;
}

I have tested this and it works correctly.

Solution 4 - C#

Additionally if someone have a use case where he needs group names before executing search on Regex object he can use:

var regex = new Regex(pattern); // initialized somewhere
// ...
var groupNames = regex.GetGroupNames();

Solution 5 - C#

This answers improves on Rashmi Pandit's answer, which is in a way better than the rest because that it seems to completely resolve the exact problem detailed in the question.

The bad part is that is inefficient and not uses the IgnoreCase option consistently.

Inefficient part is because regex can be expensive to construct and execute, and in that answer it could have been constructed just once (calling Regex.IsMatch was just constructing the regex again behind the scene). And Match method could have been called only once and stored in a variable and then linkand name should call Result from that variable.

And the IgnoreCase option was only used in the Match part but not in the Regex.IsMatch part.

I also moved the Regex definition outside the method in order to construct it just once (I think is the sensible approach if we are storing that the assembly with the RegexOptions.Compiled option).

private static Regex hrefRegex = new Regex("<td>\\s*<a\\s*href\\s*=\\s*(?:\"(?<link>[^\"]*)\"|(?<link>\\S+))\\s*>(?<name>.*)\\s*</a>\\s*</td>",  RegexOptions.IgnoreCase | RegexOptions.Compiled);

public static bool TryGetHrefDetails(string htmlTd, out string link, out string name)
{
	var matches = hrefRegex.Match(htmlTd);
	if (matches.Success)
	{
		link = matches.Result("${link}");
		name = matches.Result("${name}");
		return true;
	}
	else
	{
		link = null;
		name = null;
		return false;
	}
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUnkwnTechView Question on Stackoverflow
Solution 1 - C#Paolo TedescoView Answer on Stackoverflow
Solution 2 - C#Andrew HareView Answer on Stackoverflow
Solution 3 - C#SO UserView Answer on Stackoverflow
Solution 4 - C#tinamouView Answer on Stackoverflow
Solution 5 - C#Mariano DesanzeView Answer on Stackoverflow