简体   繁体   中英

Regex to match whole numbers

I have input string of:

"Monday 11:30am,11 v 2,3 v 4"

I need to replace:

  • 11 with Hurricanes
  • 2 with Team abc
  • 3 with Vipers
  • 4 with Dodgers
  • 1 with Frozen Rope

and so on....

But I don't want to replace the 1 twice in 11 .

I clearly don't have a grasp of regex. But I tried something like this:

string text = File.ReadAllText(CSVpath);
text = Regex.Replace(text,
                     string.Format(@"[{0} v]", CurrentCode),
                     string.Format(@"{0} v", TeamName));
text = Regex.Replace(text,
                     string.Format(@"[v {0}]", CurrentCode),
                     string.Format(@"v {0}", TeamName));

Based on that input string above, the output would be:

"Monday 11:30am,Hurricanes v Team abc,Vipers v Dodgers"

Try this regex:

var subject = "Monday 11:30am,11 v 2,3 v 4,5 v 6";
var replaced = Regex.Replace(subject,@"(\d+)\s*v\s*(\d+)","Team$1 with Team$2");

Pattern break up:

  • (\\d+) captures sequence of numbers and save it in group1
  • \\s*v\\s* checks v character wrapper by zero or more spaces both the sides.
  • (\\d+) captures sequence of numbers and save it in group2

Substitution:

$1 replaces the first group with Team and the match of first group. So 11 will be replaced with Team11, then adds up a literal with and a Team literal with $2 appends the match of group2.

Here is Regex101 demo

这应该可以解决问题: \\d{1,2} (请注意末尾的空字符!)

From http://msdn.microsoft.com/en-us/library/20bw873z(v=vs.110).aspx , which seems to be mostly in line with regex syntax in other languages, you can use "\\d+" to match one or more decimal digits.

I didn't construct the entire correct regex for you since your question was only about matching the given single pattern (11 instead of 1).

Your initial question read as though you wanted the fixed format of "Teamnn" for the output, but your edit changed that significantly, such that you want replacements using team names.

The first thing to do is define your lookup from team number to team name. I have done that here with a simple array of a "trivial" 1 class, but you might choose to have anything to store them, perhaps even a database if the numbers get sufficiently large, though I doubt that's the case here. What's important is that it's a lookup table.

class Team
{
    public string Id { get; set; }
    public string Name { get; set; }
}
static Team[] teams = { 
    new Team { Id = "11", Name = "Hurricanes" },
    new Team { Id = "2",  Name = "Team abc" },
    new Team { Id = "3",  Name = "Vipers" },
    new Team { Id = "4",  Name = "Dodgers" },
    new Team { Id = "1",  Name = "Frozen Rope" },
};

Now define the pattern matching. The regular expression

Regex regex = new Regex(@"(\d+)\s*v\s*(\d+)");

will match:

  • (\\d+) - a sequence of digits, and capture it for replacing later
  • \\s* - possibly some spaces
  • v - the letter v
  • \\s* - possibly some more spaces
  • (\\d+) - another sequence of digits, and capture it for replacing later

Note that the two sequences of digits - the team numbers - will be captured. Because of how regular expressions in .NET work, the whole string for this fixture will be captured as group 0, the first team number will be group 1, and the second team number will be group 2.

public string Test()
{
    string text = "Monday 11:30am,11 v 2,3 v 4";
    foreach (Match match in regex.Matches(text))
    {
        // For the first team in the fixture:
        string team1Id = match.Groups[1].Value; // get the id from the text
        Team team1 = teams.FirstOrDefault(t => t.Id == team1Id); // look it up
        string team1Name = team1.Name; // get the team name
        // For the second team in the fixture:
        string team2Id = match.Groups[2].Value; // get the id from the text
        Team team2 = teams.FirstOrDefault(t => t.Id == team2Id); // look it up
        string team2Name = team2.Name; // get the team name
        // Replace the whole matched string (with numbers)...
        string fixtureWithNumbers = match.Groups[0].Value;
        // ... with the equivalent with team names.
        string fixtureWithNames = team1Name + " v " + team2Name;
        text = text.Replace(fixtureWithNumbers, fixtureWithNames);
    }
    return text;
}

That is written out longhand. You can of course reduce it to something slightly smaller, like this:

public string Test2(string text)
{
    foreach (Match m in Regex.Matches(text, @"(\d+)\s*v\s*(\d+)"))
    {
        text = text.Replace(match.Groups[0].Value,
                            teams.FirstOrDefault(t => t.Id == m.Groups[1].Value)
                                 .Name +
                            " v " +
                            teams.FirstOrDefault(t => t.Id == m.Groups[2].Value)
                                 .Name);
    }
    return text;
}

though to no real gain, and possibly at the cost of a little readability or maintainability, commodities which are often undervalued.

The alternative approach is to replace the foreach loop and have the looping done within the Regex class by way of a MatchEvaluator . (My thanks to Dalorzo for suggesting this approach). For this, I have changed the regular expression to ((?<T>\\d+)(?=\\s*v))|((?<=v\\s*)(?<T>\\d+)) :

  • (?<T>\\d+) - a sequence of digits, and capture it as a named item (T) for replacing later
  • (?=\\s*v) - where it is followed by some (or none) white space and a letter v
  • | -or-
  • (?<=v\\s*) - where it is preceded by a letter v followed by some (or none) white space
  • (?<T>\\d+) - a sequence of digits, and capture it as a named item (T) for replacing later

The lookahead and lookbehind expressions assume that the name or a day will never end with the letter v (true in English, and probably at least a few other languages, but not necessarily in all), but at the same time allow for variable whitespace either wide of the v between the team numbers. If that whitespace is definitely fixed then that can be used to improve the lookahead and lookbehind strings.

The other change here is that I create a Dictionary of the teams from the array created earlier, something which would have a noticeable effect only if the number of teams became relatively large. The visible effect is that a simple dictionary lookup is done instead of a linear scan of the array for each team.

The resulting code then looks like this:

static Dictionary<string, string> teamDb =
    teams.ToDictionary(t => t.Id, t => t.Name);
public static void Test3(string text)
{
    var result = Regex.Replace(text,
                                @"((?<T>\d+)(?=\s*v))|((?<=v\s*)(?<T>\d+))",
                                new MatchEvaluator(
                                    m => teamDb[m.Groups["T"].Value]
                                ));
    Console.WriteLine("Test3: {0}", result);
}

In all three cases, the code still has an unresolved issue if the input string contains a team number which does not appear in the lookup table (the program will crash with a NullReferenceException when attempting to get Name ) but resolving that would require a decision on what you wanted to put in the output string if that happens.

--
1 There's no such thing as a trivial program.

Here is my version:

/((\d+)(\b)(?![:]))/g

Which will work as:

string text = "Monday 11:30am,11 v 2,3 v 4,5 v 6";
var result = Regex.Replace(text, @"((\d+)(\b)(?![:]))", "Team$1");

Online Demo

\\d+ will capture as many numbers as possible.
(\\b)(?![:]) excludes the numbers associated with the time by searching for : .
(\\b) is required because of what is known as the greedy behavior of + . In order to make sure that look ahead evaluates the whole number \\b is required.

Or based on your edit :

string text = "Monday 11:30am,11 v 2,3 v 4,5 v 6";
var result = Regex.Replace(text, @"((\b)([\d]+)(\b)(?![:]))", new MatchEvaluator(
delegate(Match m)
{
    return "Team" + m.Value;   /*take value and find the proper team;*/
} ));

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