如何将整个字符串匹配为具有单个正则表达式的两种格式之一?

Posted

技术标签:

【中文标题】如何将整个字符串匹配为具有单个正则表达式的两种格式之一?【英文标题】:How to match entire string to be one of two formats with a single regular expression? 【发布时间】:2018-02-28 14:59:19 【问题描述】:

我需要验证可以具有两种格式之一的值,并且正在尝试使用单个正则表达式来执行此操作,但无法弄清楚为什么它不起作用。

第一种格式正好是 17 个字母数字字符,表达式 ^[A-Za-z0-9]17$ 正确匹配测试值 5UXWX7C56BA123456,但不匹配缩短值 5UXWX7C56BA12345 或延长值 5UXWX7C56BA1234569

第二种格式正好是 8 个字母数字字符,后跟星号或下划线以及另外两个字母数字字符。表达式^[A-Za-z0-9]8[*_][A-Za-z0-9]2$ 正确匹配测试值5UXWX7C5*BA,但不匹配缩短值5UXWX7C5*B 或延长值5UXWX7C5*BA1

但是,当我尝试组合表达式时,会得到不同的意外结果,具体取决于我首先放置的子表达式。下面sn-p的代码演示

var pattern1 = new Regex(@"^([A-Za-z0-9]17)|([A-Za-z0-9]8[*_][A-Za-z0-9]2)$");
var pattern2 = new Regex(@"^([A-Za-z0-9]8[*_][A-Za-z0-9]2)|([A-Za-z0-9]17)$");

var values = new string[] 
 
    "5UXWX7C56BA12345", "5UXWX7C56BA123456", "5UXWX7C56BA1234569", 
    "5UXWX7C5*B", "5UXWX7C5*BA", "5UXWX7C5*BA1" 
;

Console.WriteLine($"Using pattern1\n");
Console.WriteLine($"  "Value",-20"IsMatch",-9"Expected",-10");
Console.WriteLine($"  new string('-', 37)");
values
    .Select(x => new  Value = x, Result = pattern1.IsMatch(x), ExpectedResult = x.Length == 11 || x.Length == 17 )
    .Select(x => $"  x.Value,-20x.Result,-9x.ExpectedResult (x.Result == x.ExpectedResult ? "" : "UNEXPECTED")")
    .WithEach(Console.WriteLine);

Console.WriteLine($"\n\nUsing pattern2\n");
Console.WriteLine($"  "Value",-20"IsMatch",-9"Expected",-10");
Console.WriteLine($"  new string('-', 37)");
values
    .Select(x => new  Value = x, Result = pattern2.IsMatch(x), ExpectedResult = x.Length == 11 || x.Length == 17 )
    .Select(x => $"  x.Value,-20x.Result,-9x.ExpectedResult (x.Result == x.ExpectedResult ? "" : "UNEXPECTED")")
    .WithEach(Console.WriteLine);

产生以下结果

Using ^([A-Za-z0-9]17)|([A-Za-z0-9]8[*_][A-Za-z0-9]2)$

  Value               IsMatch  Expected  
  -------------------------------------
  5UXWX7C56BA12345    False    False 
  5UXWX7C56BA123456   True     True 
  5UXWX7C56BA1234569  True     False UNEXPECTED
  5UXWX7C5*B          False    False 
  5UXWX7C5*BA         True     True 
  5UXWX7C5*BA1        False    False 


Using ^([A-Za-z0-9]8[*_][A-Za-z0-9]2)|([A-Za-z0-9]17)$

  Value               IsMatch  Expected  
  -------------------------------------
  5UXWX7C56BA12345    False    False 
  5UXWX7C56BA123456   True     True 
  5UXWX7C56BA1234569  True     False UNEXPECTED
  5UXWX7C5*B          False    False 
  5UXWX7C5*BA         True     True 
  5UXWX7C5*BA1        True     False UNEXPECTED

我希望有人能够指出我表达中的错误。似乎虽然我正在使用 ^ 和 $ 来尝试强制匹配整个行/值,但不知何故,当找到更长的匹配时,即使有一个我预计会导致整个值不匹配的进一步不匹配的字符匹配。

虽然我使用 LINQPad 运行上面的 sn-p,但我从 regex101.com 看到的结果相同。

【问题讨论】:

我不是正则表达式专家,但在我看来,您有一个匹配字符串开头 结尾的模式,而不是位于开始结束。 【参考方案1】:

您的正则表达式未正确锚定:

^([A-Za-z0-9]17)|([A-Za-z0-9]8[*_][A-Za-z0-9]2)$
 ^               ^ ^                                ^                

这里,([A-Za-z0-9]17) 仅锚定在字符串的开头(并且可以在该模式之后有任何内容),([A-Za-z0-9]8[*_][A-Za-z0-9]2) 仅锚定在字符串的末尾(并且可以在该模式之前有任何内容) .

第二个模式也有同样的问题,你只是交换了替代方案。

使用

var pattern1 = new Regex(@"^(?:[A-Za-z0-9]17|[A-Za-z0-9]8[*_][A-Za-z0-9]2)$");
                            ^                 ^                                ^

否则,您的替代方案不会固定在双方

请参阅regex demo。

【讨论】:

谢谢。我现在明白我做错了什么。今天学习了更多关于正则表达式的知识。

以上是关于如何将整个字符串匹配为具有单个正则表达式的两种格式之一?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用正则表达式

正则表达式中^的两种意思

正则表达式基础

《正则表达式必知必会》读书笔记

LeetCode第十题-正则表达式匹配

简单理解正则表达式