正则表达式 - C#与Perl / Python的行为不同

6

在Python中:

ttsiod@elrond:~$ python
>>> import re
>>> a='This is a test'
>>> re.sub(r'(.*)', 'George', a)
'George'

在Perl中:

ttsiod@elrond:~$ perl
$a="This is a test";
$a=~s/(.*)/George/;
print $a;
(Ctrl-D)

George

在C#中:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;

namespace IsThisACsharpBug
{
  class Program
  {
    static void Main(string[] args)
    {
        var matchPattern = "(.*)";
        var replacePattern = "George";
        var newValue = Regex.Replace("This is nice", matchPattern, replacePattern);
        Console.WriteLine(newValue);
    }
  }
}

不幸的是,C# 打印:

$ csc regexp.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.5420
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

$ ./regexp.exe 
GeorgeGeorge

这是C#正则表达式库的一个bug吗?为什么会打印"George"两次,而Perl和Python只会打印一次呢?


3
我认为答案是因为每种语言都有其自己的正则表达式实现方式,因此一个正则表达式的行为取决于运行它的引擎。 - as-cii
命令行编译加1。 - fjdumont
4个回答

6
在您的示例中,差异似乎在于“replace”函数的语义,而不是正则表达式处理本身。
.NET正在执行“全局”替换,即它正在替换所有匹配项,而不仅仅是第一个匹配项。
Perl中的全局替换
(注意=~s行末尾的小写“g”)
$a="This is a test";
$a=~s/(.*)/George/g;
print $a;

生成

GeorgeGeorge

.NET中的单个替换

var re = new Regex("(.*)");
var replacePattern = "George";
var newValue = re.Replace("This is nice", replacePattern, 1) ;
Console.WriteLine(newValue);

这将产生

George

由于它在第一次替换后就停止了。


+1 正确!他可以很容易地看到:在 Perl 下 $a=~s/(.)/X/; 给出 Xhis is a test - xanatos

3

我不确定这是不是一个bug,但如果你把.*改为.+,它就能实现你想要的功能。我猜测问题出在(.*)匹配了一个空字符串,这让事情变得混乱。

以下代码支持我的猜测:

using System;
using System.Text.RegularExpressions;

class Test
{
    static void Main()
    {
        var match = Regex.Match("abc", "(.*)");
        while (match.Success)
        {
            Console.WriteLine(match.Length);
            match = match.NextMatch();
        }
    }
}

这将打印出3然后是0。将模式更改为"(.+)"则只会打印出3。

需要注意的一点是,这与C#作为一种语言无关 - 只与.NET标准库有关。值得区分语言和库 - 例如,如果您从F#,VB,C ++ / CLI等使用.NET标准库,则会获得完全相同的行为。


2
替换 """George" (.* 匹配 "")。
和。
"This is a start" == "This is a start" + "" 

因此,正则表达式将匹配"This is a start"并用"George"替换它,现在它的“光标”位于字符串的末尾,他再次尝试使用该模式匹配剩余的字符串("")。他有一个匹配项,因此添加了第二个"George"。我不知道这是否正确。
我想补充一下,在IE和Chrome下测试(在此处测试:http://www.regular-expressions.info/javascriptexample.html),Javascript引擎似乎也会做同样的事情。

我认为实际上是起始位置引起了问题。如果你将正则表达式改为 ^(.*),问题就会得到解决。 - corylulu

2
这是 C# 正则表达式库中的一个 bug 吗?也许是,但这并不能真正回答你的问题:C# 的正则表达式与 Perl/Python 表现不同。不同的正则表达式引擎和实现确实会有所不同。有时候这是显式的(包括支持不同的正则表达式元素和语法,例如使用 \(\) 进行分组而不是使用带反斜杠的普通括号进行分组)。《精通正则表达式》(Jeffrey E.F. Friedl, O'Reilly)这本书花费了很多时间解释这些差异(除了非确定性有限状态自动机(NFA)和确定性有限状态自动机(DFA)方法之间更基本的差异)。顺便说一下,正如其他人指出的,.* 匹配空字符串,因此首先匹配并替换您输入字符串的所有内容,然后匹配并替换输入末尾的空字符串。如果要匹配整个(但可能为空的)输入,请包括开头和结尾的锚定:^(.*)$

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接