Lookahead断言作为.NET正则表达式的条件子模式的条件

时间:2012-01-27 06:31:27

标签: php .net regex pcre

我有这个巨大的RegEx用于匹配信用卡号码。但它的PCRE。在PHP中完美运行。

/(\d{13,16})(?(?=<)<|["']).*?(?=(?(?=>)>|["\'])\d{3,4}(?(?=<)<|["']))(?(?=>)>|["'])(\d{3,4})(?(?=<)<|["'])/is
// /i = ignore case
// /s = treat the subject as a single line

我将它转换为.NET。刚刚在开头添加@并将双引号加倍。我认为这是正确的程序。

@"(\d{13,16})(?(?=<)<|[""]).*?(?=(?(?=>)>|[""])\d{3,4}(?(?=<)<|[""]))(?(?=>)>|[""])(\d{3,4})(?(?=<)<|[""])"

现在它不匹配。我知道PCRE和.NET实现可能不一样。但我认为我可以将其转换为兼容的。我查看MSDN reference。看来我的模式没有什么特别的可能是PCRE特有的。

在分析模式后,我发现(?(?=<)<|[""])不匹配!因此使正则表达式更简单。它现在@"(?(?=q)qu|\w)\w+"。我正在匹配"Queen, Quick, Qi etc"

PHP

代码

$data =  "Queen, Quick, Qi etc";
$pattern = "(?(?=q)qu|\w)\w+";
preg_match_all("/$pattern/is", $data, $matches);
print_r($matches);

输出

Array
(
    [0] => Array
        (
            [0] => Queen
            [1] => Quick
            [2] => etc
        )
)

C#.NET

代码

        string data = "Queen, Quick, Qi etc";
        string pattern = @"(?(?=q)qu|\w)\w+";
        Regex re = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);

        foreach (Match m in re.Matches(data))
        {
            if (m.Success)
            {
                //Console.WriteLine("Credit Card Number={0}, CCV={1}", m.Groups[1].Value, m.Groups[6].Value);
                for (int i = 1; i < m.Groups.Count; i++)
                {
                    Console.WriteLine("[{0}][{1}]", i, m.Groups[i].Value);
                    for (int j = 0; j < m.Groups[i].Captures.Count; j++)
                    {
                        Console.WriteLine("[{0}][{1}][{2}]", i, m.Groups[i].Value, m.Groups[i].Captures[j].Value);
                    }
                }
            }
        }

输出

Press any key to continue . . .

输出无效。

我的问题是

  1. 在.NET正则表达式的条件子模式中,先行断言是否为条件?
  2. 如何修改更简单的正则表达式@"(?(?=q)qu|\w)\w+",使其与.NET中的PHP匹配?
  3. 在.NET上的第一个正则表达式(巨大的一个)上,有什么我可以应用的,所以它就像PHP一样匹配吗?
  4. 由于

1 个答案:

答案 0 :(得分:2)

1:条件语在.NET中工作就像在PHP中一样。

2:“更简单”的正则表达式对于.NET是正确的。你刚才错了:

你的正则表达式中没有捕获组。这意味着循环

for (int i = 1; i < m.Groups.Count; i++) {...}

永远不会被执行,因为m.Groups.Count是1。

正确的方法是

foreach (Match m in re.Matches(data))
{
   if (m.Success)
   {
       for (int i = 0; i < m.Groups.Count; i++) // Groups are numbered from zero
       {
           // Groups[0] is the entire match
           Console.WriteLine("[{0}][{1}]", i, m.Groups[i].Value);
       }
   }
} 

3:你的正则表达式缺少单引号。

Regex regexObj = new Regex(@"(\d{13,16})(?(?=<)<|[""']).*?(?=(?(?=>)>|[""'])\d{3,4}(?(?=<)<|[""']))(?(?=>)>|[""'])(\d{3,4})(?(?=<)<|[""'])", RegexOptions.Singleline);

将是字面翻译。

4。:您不需要/iIgnorecase参数,因为正则表达式中没有字母。

5:(?(?=<)<|["'])毫无意义。它与[<"']完全匹配相同的文本。毕竟,这意味着“如果有<,则匹配<。否则,请尝试匹配"'。无需使用有条件的正则表达式。

因此整个正则表达式可以简化为

(\d{13,16})[<"'].*?(?=[>"']\d{3,4}[<"'])[>"'](\d{3,4})[<"']

6:这更清楚地显示了正则表达式的另一个多余部分:你有一个先行断言(?=[>"']\d{3,4}[<"']),后跟完全相同的正则表达式[>"'](\d{3,4})[<"'],因此可以完全删除前瞻。

最终结果:

(\d{13,16})[<"'].*?[>"'](\d{3,4})[<"']

或者,在C#中:

Regex regexObj = new Regex(@"(\d{13,16})[<""'].*?[>""'](\d{3,4})[<""']", RegexOptions.Singleline);