优化正则表达式以读取日期
Optimize regex to read date
我开发了一个在 .NET WebAPI 中使用的正则表达式,它从已格式化为最终格式的给定输入获取日期和控制代码。
我尝试使用正则表达式来避免使用多个字符串拆分。
我一直在使用 Regex101 来测试我的表达式,我有一个已经按预期工作了,但我认为它对于它的功能来说太大了。
表达式:
^([0-9]{2})+([0-9]{2})+([0-9]{2})[0-9](M|F)([0-9]{2})+([0-9]{2})+([0-9]{2})
// 获取年、月、日、代码(M|F)、年、月、日
输入:
7603259M2209058PRT<<<<<<<<<<<8
你有什么简化的建议吗?
您的正则表达式存在一个问题:您使用 +
量词量化了两位数的匹配捕获组,使它们匹配了一次或多次。 ([0-9]{2})+
匹配任意两个 ASCII 数字的一个或多个序列,同时将最后捕获的值保留在相应的组中。参见 Repeating a Capturing Group vs. Capturing a Repeated Group。
您需要从模式中删除所有 +
个字符,然后您还可以使用以下内容:
- 使用
\d
匹配任何数字,同时将RegexOptions.ECMAScript
选项传递给正则表达式编译方法,使其只能匹配ASCII数字(否则,\d
将等于\p{Nd}
并将匹配任何 Unicode 数字,请参阅 \d less efficient than [0-9])
- 不要使用单个字符 (
(M|F)
),而是使用字符 class、([MF])
,这样效率更高(参见 Why is a character class faster than alternation?)。
您可以使用
var pattern = new Regex(@"^(\d{2})(\d{2})(\d{2})\d([MF])(\d{2})(\d{2})(\d{2})", RegexOptions.ECMAScript);
如果您想使用更短的正则表达式,您可以使用:
var pattern = new Regex(@"^(?:(\d{2})){3}\d([MF])(?:(\d{2})){3}", RegexOptions.ECMAScript);
var match = pattern.Match("7603259M2209058PRT<<<<<<<<<<<8");
if (match.Success)
{
Console.WriteLine(match.Groups[1].Captures[0].Value); // => 76
Console.WriteLine(match.Groups[1].Captures[1].Value); // => 03
Console.WriteLine(match.Groups[1].Captures[2].Value); // => 25
Console.WriteLine(match.Groups[2].Value); // => M
Console.WriteLine(match.Groups[3].Captures[0].Value); // => 22
Console.WriteLine(match.Groups[3].Captures[1].Value); // => 09
Console.WriteLine(match.Groups[3].Captures[2].Value); // => 05
}
参见C# demo and this regex demo。
请注意,这是可能的,因为 .NET Regex
允许访问组堆栈内的所有捕获。
我开发了一个在 .NET WebAPI 中使用的正则表达式,它从已格式化为最终格式的给定输入获取日期和控制代码。
我尝试使用正则表达式来避免使用多个字符串拆分。
我一直在使用 Regex101 来测试我的表达式,我有一个已经按预期工作了,但我认为它对于它的功能来说太大了。
表达式:
^([0-9]{2})+([0-9]{2})+([0-9]{2})[0-9](M|F)([0-9]{2})+([0-9]{2})+([0-9]{2})
// 获取年、月、日、代码(M|F)、年、月、日
输入:
7603259M2209058PRT<<<<<<<<<<<8
你有什么简化的建议吗?
您的正则表达式存在一个问题:您使用 +
量词量化了两位数的匹配捕获组,使它们匹配了一次或多次。 ([0-9]{2})+
匹配任意两个 ASCII 数字的一个或多个序列,同时将最后捕获的值保留在相应的组中。参见 Repeating a Capturing Group vs. Capturing a Repeated Group。
您需要从模式中删除所有 +
个字符,然后您还可以使用以下内容:
- 使用
\d
匹配任何数字,同时将RegexOptions.ECMAScript
选项传递给正则表达式编译方法,使其只能匹配ASCII数字(否则,\d
将等于\p{Nd}
并将匹配任何 Unicode 数字,请参阅 \d less efficient than [0-9]) - 不要使用单个字符 (
(M|F)
),而是使用字符 class、([MF])
,这样效率更高(参见 Why is a character class faster than alternation?)。
您可以使用
var pattern = new Regex(@"^(\d{2})(\d{2})(\d{2})\d([MF])(\d{2})(\d{2})(\d{2})", RegexOptions.ECMAScript);
如果您想使用更短的正则表达式,您可以使用:
var pattern = new Regex(@"^(?:(\d{2})){3}\d([MF])(?:(\d{2})){3}", RegexOptions.ECMAScript);
var match = pattern.Match("7603259M2209058PRT<<<<<<<<<<<8");
if (match.Success)
{
Console.WriteLine(match.Groups[1].Captures[0].Value); // => 76
Console.WriteLine(match.Groups[1].Captures[1].Value); // => 03
Console.WriteLine(match.Groups[1].Captures[2].Value); // => 25
Console.WriteLine(match.Groups[2].Value); // => M
Console.WriteLine(match.Groups[3].Captures[0].Value); // => 22
Console.WriteLine(match.Groups[3].Captures[1].Value); // => 09
Console.WriteLine(match.Groups[3].Captures[2].Value); // => 05
}
参见C# demo and this regex demo。
请注意,这是可能的,因为 .NET Regex
允许访问组堆栈内的所有捕获。