正则表达式中的规则与命名组重叠
本文关键字:重叠 规则 正则表达式 | 更新日期: 2023-09-27 18:29:39
我遇到了解析自定义电话号码的正则表达式的问题:
- 与"wtvCode"组匹配的值是可选的
- 与"countryCode"组匹配的值是可选的
- countryCode规则与areaCityCode规则在某些值上重叠。在这种情况下,当countryCode丢失时,其表达式将捕获areaCityCode值
下面是代码示例。
Regex regex = new Regex(string.Concat(
"^(",
"(?<wtvCode>[A-Z]{3}|)",
"([-|/|#| |]|)",
"(?<countryCode>[2-9+]{2,5}|)",
"([-|/|#| |]|)",
"(?<areaCityCode>[0-9]{2,3}|)",
"([-|/|#| |]|))",
"(?<phoneNumber>(([0-9]{8,18})|([0-9]{3,4}([-|/|#| |]|)[0-9]{4})|([0-9]{4}([-|/|#| |]|)[0-9]{4})|([0-9]{4}([-|/|#| |]|)[0-9]{4}([-|/|#| |]|)[0-9]{1,5})))",
"([-|/|#| |]|)",
"(?<foo>((A)|(B)))",
"([-|/|#| |]|)",
"(?<bar>(([1-9]{1,2})|)",
")$"
));
string[] validNumbers = new[] {
"11-1234-5678-27-A-2", // missing wtvCode and countryCode
"48-1234-5678-27-A-2", // missing wtvCode and countryCode
"55-48-1234-5678-27-A-2" // missing wtvCode
};
foreach (string number in validNumbers) {
Console.WriteLine("countryCode: {0}", regex.Match(number).Groups["countryCode"].Value);
Console.WriteLine("areaCityCode: {0}", regex.Match(number).Groups["areaCityCode"].Value);
Console.WriteLine("phoneNumber: {0}", regex.Match(number).Groups["phoneNumber"].Value);
}
其输出为:
// First number
// countryCode: <- correct
// areaCityCode: 11 <- correct, but that's because "11" is never a countryCode
// phoneNumber: 1234-5678-27 <- correct
// Second number
// countryCode: 48 <- wrong, should be ""
// areaCityCode: <- wrong, should be "48"
// phoneNumber: 1234-5678-27 <- correct
// Third number
// countryCode: 55 <- correct
// areaCityCode: 48 <- correct
// phoneNumber: 1234-5678-27 <- correct
到目前为止,我还没有成功地修复这个正则表达式,因为它覆盖了我的所有约束,并且当一个值与两个规则匹配时,不会干扰countryCode和areaCityCode。有什么想法吗?
提前谢谢。
更新
手机国家/地区代码的正确正则表达式模式可以在此处找到:https://stackoverflow.com/a/6967885/136381
首先,我建议使用?
量词来使事情成为可选的,而不是您现在使用的空选项。在国家代码的情况下,添加另一个?
使其不贪婪。这样,它最初将尝试捕获areaCityCode
组中的第一组数字。只有当整个匹配失败时,它才会返回并使用countryCode
组。
Regex regex = new Regex(
@"^
( (?<wtvCode>[A-Z]{3}) [-/# ] )?
( (?<countryCode>[2-9+]{2,5}) [-/# ] )??
( (?<areaCityCode>[0-9]{2,3}) [-/# ] )?
(?<phoneNumber> [0-9]{8,18} | [0-9]{3,4}[-/# ][0-9]{4}([-/# ][0-9]{1,5})? )
( [-/# ] (?<foo>A|B) )
( [-/# ] (?<bar>[1-9]{1,2}) )?
$",
RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture);
正如您所看到的,我对您的代码做了一些其他更改,最重要的是从([-|/|#| |]|)
切换到[-/# ]
。括号内的管道将只匹配字面上的|
,我敢肯定你不想要。最后一根管道使分离器成为可选;我希望他们不是真的必须是可选的,因为这会让这份工作变得更加困难。
您和其他响应者忽略了两件事。
首先,反向工作更有意义,换句话说,从右到左,因为文本末尾需要的字段比开头多。通过消除对WTV和Country代码的怀疑,正则表达式解析器的工作变得更加容易(,尽管编写模式的人在智力上更难)。
第二个是在regex(?()|())中使用if条件。这使我们能够测试一个场景,并在另一个场景上实现一种匹配模式。我在我的博客"正则表达式和if条件"中描述了if条件。下面的模式测试是否存在WTV&国家,如果是,它匹配,如果不是,它检查一个可选的国家。
同样,与其连接模式,为什么不使用IgnorePatternHitespace来允许对模式进行注释,如下所示:
string pattern = @"
^
(?([A-Z][^'d]?'d{2,5}(?:[^'d])) # If WTV & Country Code (CC)
(?<wtvCode>[A-Z]{3}) # Get WTV & CC
(?:[^'d]?)
(?<countryCode>'d{2,5})
(?:[^'d]) # Required Break
| # else maybe a CC
(?<countryCode>'d{2,5})? # Optional CC
(?:[^'d]?) # Optional Break
)
(?<areaCityCode>'d'd'd?) # Required area city
(?:[^'d]?) # Optional break (OB)
(?<PhoneStart>'d{4}) # Default Phone # begins
(?:[^'d]?) # OB
(?<PhoneMiddle>'d{4}) # Middle
(?:[^'d]?) # OB
(?<PhoneEnd>'d'd) # End
(?:[^'d]?) # OB
(?<foo>[AB]) # Foo?
(?:[^AB]+)
(?<bar>'d)
$
";
var validNumbers = new List<string>() {
"11-1234-5678-27-A-2", // missing wtvCode and countryCode
"48-1234-5678-27-A-2", // missing wtvCode and countryCode
"55-48-1234-5678-27-A-2", // missing wtvCode
"ABC-501-48-1234-5678-27-A-2" // Calling Belize (501)
};
validNumbers.ForEach( nm =>
{
// IgnorePatternWhitespace only allows us to comment the pattern; does not affect processing
var result = Regex.Match(nm, pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.RightToLeft).Groups;
Console.WriteLine (Environment.NewLine + nm);
Console.WriteLine("'tWTV code : {0}", result["wtvCode"].Value);
Console.WriteLine("'tcountryCode : {0}", result["countryCode"].Value);
Console.WriteLine("'tareaCityCode: {0}", result["areaCityCode"].Value);
Console.WriteLine("'tphoneNumber : {0}{1}{2}", result["PhoneStart"].Value, result["PhoneMiddle"].Value, result["PhoneEnd"].Value);
}
);
结果:
11-1234-5678-27-A-2
WTV code :
countryCode :
areaCityCode: 11
phoneNumber : 1234567827
48-1234-5678-27-A-2
WTV code :
countryCode :
areaCityCode: 48
phoneNumber : 1234567827
55-48-1234-5678-27-A-2
WTV code :
countryCode : 55
areaCityCode: 48
phoneNumber : 1234567827
ABC-501-48-1234-5678-27-A-2
WTV code : ABC
countryCode : 501
areaCityCode: 48
phoneNumber : 1234567827
注:
- 如果在国家代码和城市代码之间没有分隔符,解析程序无法确定什么是城市,什么是国家
- 您的原始国家/地区代码模式失败[2-9]这个国家有一个0。所以我把它改成[2-90]