为啥 ^*$ 匹配“127.0.0.1”

Posted

技术标签:

【中文标题】为啥 ^*$ 匹配“127.0.0.1”【英文标题】:Why ^*$ matches "127.0.0.1"为什么 ^*$ 匹配“127.0.0.1” 【发布时间】:2010-09-18 07:16:00 【问题描述】:

我不明白,为什么下面的正则表达式:

^*$

匹配字符串“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();
        
    

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

编辑2: 消除任何混乱。我写这个正则表达式并不是为了让它匹配“127.0.0.1”。我们应用程序的用户输入了表达式并想知道为什么它不应该匹配字符串。看了之后,我无法解释为什么它匹配 - 特别是因为 Expresso 和 .NET 似乎处理它的方式不同。

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

【问题讨论】:

你的正则表达式应该匹配什么?您的正则表达式没有多大意义。 【参考方案1】:

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

根据 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 那样工作。

【讨论】:

那么鉴于此,我们是否可以断定这是 .NET 实现中的错误? 为什么你认为c#遵循SUS标准? 大声笑,当几个标准相互竞争时,这让我很开心,这反过来又使“标准”这个词自相矛盾。 它不会将单个星号视为“.*”,因为它不匹配整个字符串。它在索引 9 处匹配,这意味着它仅匹配行尾。有意义:“^*$”是零个或多个行首(在这种情况下为零),后跟一个行尾。 实际上,您会注意到他正在使用 IsMatch。您的演示不正确。正则表达式匹配字符串标记的结尾,而不是整个字符串:它匹配,所以函数返回 true,但匹配的内容基本上是空字符串。【参考方案2】:

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

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

我真的不明白你要做什么。如果你能给我们更多信息,那么也许我可以告诉你你应该做什么:)

【讨论】:

根据 POSIX 和 ISO Regex 标准,只跟在 ^ 后面的星号没有特殊含义,只匹配星号本身! 为什么你认为c#遵循SUS标准?【参考方案3】:

如果你尝试

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

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

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

顺便说一句,MSDN 文档没有提到“*”只匹配“*”,除非转义为“\*”。而 '*' 本身会抛出异常,与 '*' 不匹配。

【讨论】:

这是一个很好的答案。那么真正的问题出在 .NET 实现允许行首字符的量词这一事实?【参考方案4】:

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

【讨论】:

错了!根据 POSIX/ISO 正则表达式标准,^ 后面的星号只匹配星号本身,因为它没有特殊含义! 出于好奇,我在哪里可以找到正则表达式的 POSIX 和 ISO 标准? 为什么你认为c#遵循SUS标准? 你在想“^.*$”的地方,不是这样的。【参考方案5】:

POSIX 正则表达式标准非常古老且有限。今天仍然遵循它的少数工具,例如 grep、sed 和朋友,主要是在 unix/linux shell 上。 Perl 和 PCRE 是两种扩展性很强的风格,POSIX 标准中几乎没有任何内容仍然适用。

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

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

【讨论】:

【参考方案6】:

除了非法的正则表达式,你想写的很可能不是那个。

你写:^*$应该匹配一个字符串/行的开头任意次数,然后是字符串/行的结尾”,这意味着你想要多行正则表达式,但是你忘记了一条线不能开始两次,中间没有线结束。

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

另外,* 尽可能匹配(除非设置了非贪婪模式),这意味着正则表达式 /^.**$/ 正则表达式将匹配所有内容。如果你想管理换行符,你必须明确地编写这些代码。

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

【讨论】:

【参考方案7】:

使用RegexDesigner,我可以看到它与“127.0.0.1”之后的“空”令牌匹配。似乎因为您没有指定令牌并且加号匹配零次或多次,所以它匹配“空”令牌。

以下正则表达式应该可以工作:

^+$

【讨论】:

不,不应该。您不能在开始锚点上重复。 奇怪。它没有错误,也不匹配,这似乎表明它有效。 参见 POSIX/ISO 正则表达式标准。仅跟在 ^ 后面的星号没有特殊含义,只匹配星号本身! 为什么你认为c#遵循SUS标准?

以上是关于为啥 ^*$ 匹配“127.0.0.1”的主要内容,如果未能解决你的问题,请参考以下文章

Vue + Laravel sanctum CSRF 令牌不匹配 419 错误

Redis笔记之常用命令

nginx匹配一个网段

其他命令

Django路由层

基于URL的正则匹配