为什么^ * $匹配“127.0.0.1”

时间:2008-10-21 11:31:10

标签: c# regex

我不明白,为什么会有以下正则表达式:

^*$

匹配字符串“127.0.0.1”?使用Regex.IsMatch("127.0.0.1", "^*$");

使用Expresso,它不匹配,这也是我所期望的。使用表达式^.*$确实匹配字符串,我也希望这样。

从技术上讲,^*$应该匹配字符串/行的开头任意次,然后是字符串/行的结尾。似乎*被隐含地视为.*

我错过了什么?

编辑: 运行以下命令以查看问题的示例。

using System;
using System.Text.RegularExpressions;

namespace RegexFubar
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Regex.IsMatch("127.0.0.1", "^*$"));
            Console.Read();
        }
    }
}

我不希望^ * $匹配我的字符串,我想知道它为什么确实匹配它。我认为该表达式应该导致抛出异常,或者至少是不匹配。

EDIT2: 澄清任何困惑。我没有写这个正则表达式,意图让它匹配“127.0.0.1”。我们的应用程序的用户输入了表达式,并想知道为什么它不应该匹配字符串。看了之后,我无法解释为什么它匹配 - 特别是因为Expresso和.NET似乎不同地处理它。

我想这个问题是由于.NET实现避免抛出异常而得到回答,甚至认为它在技术上是一个不正确的表达式。但这真的是我们想要的吗?

7 个答案:

答案 0 :(得分:27)

嗯,理论上你是对的,它不应该匹配。但这取决于实施如何在内部运作。大多数正则表达式。将从前面取你的正则表达式并剥离^(注意它必须从字符串的开头匹配)并从末尾剥离$(注意它必须到字符串的结尾),遗留的只是“* “和”*“本身就是一个有效的正则表达式。您正在使用的实现对于如何处理它是错误的。你可以试试如果用“*”替换“^ * $”会发生什么;我想它也会匹配一切。似乎实现将单个星号视为“。*”。

根据ISO / IEC 9945-2:1993标准,也在POSIX standard中描述,它已被破坏。它被打破是因为标准说在^字符之后,星号没有任何特殊含义。这意味着“^ * $”实际上只匹配一个字符串,而这个字符串是“*”

引用标准:

  

除非使用星号,否则星号是特殊的:

     
      
  • 在括号表达式
  • 中   
  • 作为整个BRE的第一个字符(在初始^之后,如果有的话)
  •   
  • 作为子表达式的第一个字符(在初始^之后,如果有的话);看BRE匹配多个字符。
  •   

因此,如果它是第一个字符(并且^如果存在则不算作第一个字符),它没有特殊含义。这意味着在这种情况下,星号应该只匹配一个字符,这是一个星号。


更新

微软说

  

Microsoft .NET Framework常规   表达式包含最多   其他常规的流行功能   表达式实现,如   那些在Perl和awk。设计为   兼容Perl 5常规   表达式,.NET Framework常规   表达式包括尚未提供的功能   在其他实现中看到,例如   从右到左的匹配和即时   汇编

来源:http://msdn.microsoft.com/en-us/library/hs600312.aspx

好的,让我们测试一下:

# echo -n 127.0.0.1 | perl -n -e 'print (($_ =~ m/(^.*$)/)[0]),"\n";'
-> 127.0.0.1
# echo -n 127.0.0.1 | perl -n -e 'print (($_ =~ m/(^*$)/)[0]),"\n";'
->

不,它没有。 Perl工作正常。 ^。* $匹配字符串,^ * $不=> .NET的正则表达式实现被破坏,它不像MS声称的那样像Perl 5那样工作。

答案 1 :(得分:9)

星号(*)匹配前面的元素 ZERO OR MORE 次。如果您想要一个或多个,请使用+运算符而不是*。

您要求它匹配字符串标记的可选开头和字符串标记的结尾。即如果我们省略字符串标记的开头,你只需要查找字符串标记的结尾...它将匹配任何字符串!

我真的不明白你想要做什么。如果您可以提供更多信息,那么我可以告诉您应该做些什么:)

答案 2 :(得分:2)

如果您尝试

Regex.Match("127.0.0.1", "^*1$")

你会看到它也匹配。 Match.Index 属性的值为8,表示它匹配最后一个'1',而不是第一个'1'。这是有道理的,因为“^ *”将匹配零个或多个行首,并且在“1”之前没有行首。

想想“a * 1 $”的匹配方式,因为“1 $”之前没有'a'。所以“a * $”会与行尾相匹配,就像你的例子一样。

顺便说一句,MSDN文档没有提到'*'只是简单地匹配'*',除非转义为'\ *'。并且'*'本身会抛出异常,而不是'*'。

答案 3 :(得分:0)

你实际上是在说“匹配一个什么都不包含的字符串”。所以它会匹配。在这种情况下,^和$绑定并没有真正区别。

答案 4 :(得分:0)

非法的正则表达式,你想要写的很可能不是那样。

你写道:“^ * $应该匹配字符串/行的开头任意次,然后是字符串/行的结尾”,这意味着你想要多行regexp,但是你忘记了一行无法启动两次,中间没有一条线。

此外,您在要求中提出的问题实际上符合“127.0.0.1”:)“^”不是换行/回车,而是行的开头,“$”不仅仅是换行但是结束了。

此外,“*”尽可能匹配(除非设置了ungreedy模式),这意味着regexp /^.**$/ regexp将匹配所有内容。如果您想管理换行符,则必须明确地对其进行编码。

希望这能澄清一些事情:)

答案 5 :(得分:0)

POSIX正则表达式标准非常陈旧且有限。今天仍然关注的几个工具,如grep,sed和friends,大多数都在unix / linux shell上。 Perl和PCRE是两种非常扩展的口味,其中POSIX标准中几乎没有提到的东西仍然适用。

http://www.regular-expressions.info/refflavors.html

在PCRE和Perl中,引擎将^$视为与字符串的开头和结尾匹配的标记(如果设置了多行标记,则为行)。 *只需重复^个标记零次或多次(在这种情况下,恰好为零次)。因此引擎只查找源字符串的末尾,它匹配任何字符串。

答案 6 :(得分:-1)

使用RegexDesigner,我可以看到它在'127.0.0.1'后面的'null'标记上匹配。似乎因为你没有指定一个令牌而加号匹配零次或多次,它匹配'null'令牌。

以下正则表达式应该有效:

^+$