简体   繁体   中英

Working With Match and Regex

I am working on a project that parses an incoming text file. I am learning C# as I go. My current method for picking out the information I need is something like:

string MyMatchString = @"a pattern to match";
Match MyMatch = Regex.Match(somestringinput, MyMatchString);
if (MyMatch.Succes)
{
   DoSomething(MyMatch.Value);
}

I am doing a lot of that. I'd like to be able to combine the match and the test for success in one step. Looking through the Class listings, Regex has an IsMatch() method, but it doesn't appear that I can access the matched value (assuming it is successful). I think I need a Match instance for that. I tried

if ((Match MyMatch = Regex.Match(somestringinput, MyMatchString).Success)

but of course got a compile error.

I am thinking a static method that takes the match pattern and the input then returns a bool is the way to go. Then I can just test for success, and if so grab the matched value.

You can use foreach

string MyMatchString = @"a pattern to match";
foreach (Match match in Regex.Matches(somestringinput, MyMatchString))
{
    DoSomething(match.Value);
}

Optionally add break if you want only one match (or use .Take(1) )

Well you can write an extension method for Regex which would give you some power. The trick is doing it without running the regex match twice, which could be a problem for your performance (note: this hasn't been tested, and it differs from your idea in that it requires a ready-made Regex object to work).

public static class RegexExtensions {
    public static bool GetMatch(this Regex regex, string input, out string matched) {
        Match match = regex.Match(input);
        if (match.Success) {
            matched = match.Value;
            return true;
        }
        matched = null;
        return false;
    }
}

So you'd do something like

string matched;
Regex regex = new Regex(pattern);
if (regex.GetMatch(inputstring, matched))
{ /* do something with 'matched' */ }
else
{ /* no match, 'matched' is null */ }

You might prefer to just return null in the failure case and the string otherwise and dispense with the boolean and the output parameter.

You could implement the "Try" convention used by TryParse and TryGetValue .

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

// usage
string myMatchString = @"a pattern to match"; 
Match result = null;
if(TryMatch(somestringinput, myMatchString, out result))
{
    DoSomething(result.Value);
}

Alternatively, you can create higher-order functions that accept Action and Func delegates.

public void ActionOnMatch(string input, string pattern, Action<string> action)
{
    var m = Regex.Match(input, pattern);
    if(m.Success)
    {
        action(m.Value);
    }
}

public TResult FuncOnMatch<TResult>(string input, string pattern, 
    Func<string, TResult> func)
{
    var m = Regex.Match(input, pattern);
    if(m.Success)
    {
        return func(m.Value);
    }
    else
    {
        return default(TResult);
    }
}

/// usage
string pattern = @"a pattern to match"; 
ActionOnMatch(input, pattern, DoSomething);
var result = FuncOnMatch<string>(input, pattern, (val) => val);

If you just want an answer as a boolean value and dont want to store it dont use a variable match to store it. You can just use Regex.Match to get the boolean value you want like this:

if (Regex.Match("a", MyMatchString).Success) { };

Creating the Match, testing for success and using the results are separate steps, by design. There might be ways around that, but you'd be fighting the framework, and possibly making your code a little less readable for other developers.

If you're parsing a file, you might consider an alternate approach using Linq. Depending on exactly what you mean by DoSomething , this might at least make the code a little less tedious.

Regex re = new Regex(@"(?<prop1>pattern part 1)(?<prop2>pattern part 2)");

var goodLines = 
    from line in File.ReadAllLines(inputFile)
    let m = re.Match(line)
    where m.Success
    select new {
        Prop1 = m.Groups["prop1"].ToString(),
        Prop2 = m.Groups["prop2"].ToString()
    }

foreach (var goodLine in goodLines) {
    DoSomething(goodLine);
}

Here we're defining named capture groups within the regex, and using those named captures to populate an anonymous type. Then we're looping over the collection of those anonymous objects returned by the Linq expression.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM