David Green David Green - 5 months ago 24
C# Question

C# Regular Expression Optional Named Group

I have this pattern:

string ISLTokenPattern = @"\d+:(\s+)?(?<local>\d+)[-][>](\s+)?(?<remote>\d+)\s+(?<wwn>..:..:..:..:..:..:..:..)\s+\d+\s(?<name>.+)\s+[s][p]:\s+\w+.\w+\s+\w+[:]\s+\d+.\w+\s+((?<trunk>TRUNK))?\s";


I have this input:

1: 0-> 20 10:00:50:eb:1a:11:e3:4e 105 cwymdsae05 sp: 16.000G bw: 64.000G TRUNK QOS CR_RECOV FEC
2: 21-> 5 10:00:50:eb:1a:12:a1:d3 108 cwymdsae08 sp: 16.000G bw: 96.000G TRUNK QOS CR_RECOV FEC
3: 32-> 0 55:0e:b1:ae:a0:20:0e:46 160 fcr_fd_160 sp: 8.000G bw: 8.000G
4: 33-> 1 55:0e:b1:ae:a0:20:0e:46 160 fcr_fd_160 sp: 8.000G bw: 8.000G
5: 66-> 46 10:00:50:eb:1a:11:e3:4e 105 cwymdsae05 sp: 16.000G bw: 64.000G


On RegExStorm.Net the pattern matches all 5 lines of input. Usually if something works there, it works in C#. In my code, the match fails on lines 3, 4 and 5. If I take off the

((?<trunk>TRUNK))?\s


at the end lines 3, 4 and 5 match, but lines 1 and 2 fail. I need it to match both. As a workaround I have 2 patterns and test for 2 matches, but I'd rather do a single pattern and 1 test.

Here is the code that does the match:

string ISLTokenPattern = @"\d+:(\s+)?(?<local>\d+)[-][>](\s+)?(?<remote>\d+)\s+(?<wwn>..:..:..:..:..:..:..:..)\s+\d+\s(?<name>.+)\s+[s][p]:\s+\w+.\w+\s+\w+[:]\s+\d+.\w+\s+((?<trunk>TRUNK))?\s";


if (RegexExtensions.TryMatch(out tokenMatch, line, ISLTokenPattern)
{
string local = tokenMatch.Groups["local"].Value;
string remote = tokenMatch.Groups["remote"].Value;
string wwn = tokenMatch.Groups["wwn"].Value.ToUpper();

string name = "";
if (tokenMatch.Groups["name"].Success)
{
name = tokenMatch.Groups["name"].Value;
}


Here is the RegExtension Class I wrote. This program parses text files and I do a lot of matches so wanted something that could match and test for success in one step to keep the code cleaner.

public static class RegexExtensions
{
public static bool TryMatch(out Match match, string input, string pattern)
{
match = Regex.Match(input, pattern);
return (match.Success);
}

public static bool TryMatch(out MatchCollection match, string input, string pattern)
{
match = Regex.Matches(input, pattern);
return (match.Count > 0);
}
}

Answer Source

One possible issue I notice with your current regex is the ending:

\s+((?<trunk>TRUNK))?\s

This matches, at the end of the regex, one or more spaces, followed by an optional named capture group for TRUNK, followed by a single space. Note that your log lines which do not have TRUNK (possibly followed by other text) only have one space. But the pattern is expecting two or more spaces. The solution you used, namely removing the final \s might work. But you could also move the space inside the optional captured group, i.e.

\s+((?<trunk>TRUNK\s))?

This would optionally match TRUNK followed by a single space. Which you use depends on your actual data.