有人看到我的regex端口号有什么问题吗?
我为端口号制作了一个正则表达式(在你说这是一个坏主意之前,这是一个更大的URL正则表达式,这比听起来要困难得多) 我的同事说这真的很糟糕,不会抓住一切。我不同意 我相信这东西能捕捉到从0到65535的所有东西,其他什么都没有,我正在寻找这一点的确认 单线版本(适用于计算机): 可读版本:有人看到我的regex端口号有什么问题吗?,regex,Regex,我为端口号制作了一个正则表达式(在你说这是一个坏主意之前,这是一个更大的URL正则表达式,这比听起来要困难得多) 我的同事说这真的很糟糕,不会抓住一切。我不同意 我相信这东西能捕捉到从0到65535的所有东西,其他什么都没有,我正在寻找这一点的确认 单线版本(适用于计算机): 可读版本: /(^[0-9]$)| # single digit (^[0-9][0-9]$)| # two digit (
/(^[0-9]$)| # single digit
(^[0-9][0-9]$)| # two digit
(^[0-9][0-9][0-9]$)| # three digit
(^[0-9][0-9][0-9][0-9]$)| # four digit
((^[0-5][0-9][0-9][0-9][0-9]$)| # five digit (up to 59999)
(^6[0-4][0-9][0-9][0-9]$)| # (up to 64999)
(^65[0-4][0-9][0-9]$)| # (up to 65499)
(^655[0-2][0-9]$)| # (up to 65529)
(^6553[0-5]$))/ # (up to 65535)
有人能确认我的理解是正确的吗?好吧,很容易证明它会验证任何正确的端口:只需生成每个有效的字符串并测试它是否通过。但是,确保它不允许任何不应该允许的内容是很困难的——显然,您不能绝对测试所有无效字符串。您肯定应该测试简单的案例和任何您认为可能不正确通过的内容(或者使用较小的正则表达式--“65536”作为示例会不正确通过的内容) 它允许一些稍微奇怪的端口规范,比如“0000”。是否允许前导零
您可能还想考虑是否需要为每种情况分别指定^和$,或者是否可以使用<代码> ^(案例1)?(案例2)……$< /代码>。哦,量词也可以简化“1到4位数”的情况:
([0-9]{1,4})
会找到1到4位数
(顺便说一句,你可能想让自己听起来不那么傲慢。如果你在和其他人一起工作,那么用不那么咄咄逼人的方式交流可能比证明你的正则表达式是正确的更有助于改善每个人的一天……。风格提示:
反复重复
[0-9]
是愚蠢的-像[0-9][0-9][0-9]
这样的东西写得更好,因为\d{3}
你可以大大缩短它:
^0*(?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$
- 无需每次重复锚定
- 不需要大量的捕获组
- 不需要重复
0*
这个正则表达式也更好,因为它首先匹配特殊情况(65535、65001等),从而避免了一些回溯
哦,既然你说你想把它作为更大的URL正则表达式的一部分,那么你应该把^
和$
都替换为\b
(单词边界锚定)
编辑:@ceving询问是否确实需要重复
6553
、655
、65
和6
。答案是否定的-您也可以使用嵌套的正则表达式,而不必重复这些前导数字。让我们来考虑一下
6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}
这可以改写为
6(?:[0-4][0-9]{3}|5(?:[0-4][0-9]{2}|5(?:[0-2][0-9]|3[0-5])))
我认为,这使得正则表达式的可读性比以前更低。详细模式使差异更加清晰。比较
6553[0-5] |
655[0-2][0-9] |
65[0-4][0-9]{2} |
6[0-4][0-9]{3}
与
一些性能度量:根据从1到99999的所有数字测试每个正则表达式,可以看出嵌套版本的最小性能优势,可能与性能无关:
import timeit
r1 = """import re
regex = re.compile(r"0*(?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$")"""
r2 = """import re
regex = re.compile(r"0*(?:6(?:[0-4][0-9]{3}|5(?:[0-4][0-9]{2}|5(?:[0-2][0-9]|3[0-5])))|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$")"""
stmt = """for i in range(1,100000):
regex.match(str(i))"""
print(timeit.timeit(setup=r1, stmt=stmt, number=100))
print(timeit.timeit(setup=r2, stmt=stmt, number=100))
输出:
7.7265428834649
7.556472630353351
就个人而言,我只会匹配一个数字,然后我会用代码检查数字是否在范围内。正则表达式有很多实现,而paltform是什么。试试下面,去掉空白
^[1-5]?\d{1,4}|6([0-4]\d{3}|5([0-4]\d{2}|5([0-2]\d|3[0-5]))$
可读的
^
[1-5]?\d{1,4}|
6(
[0-4]\d{3}|
5(
[0-4]\d{2}|
5(
[0-2]\d|
3[0-5]
)
)
$
将其解析为数字并进行整数比较有什么错?(无论这是否是“较大”正则表达式的一部分) 如果我使用正则表达式,我只会使用:
\d{1,5}
不,它不会检查“有效”端口号(您的也不会)。但它更清晰,从实用角度来说,我认为它“足够好了”
附言:我会努力变得更谦虚。我会用一个:
输出为:
Port [0 65536] => 65536 matches in 131074 tests
端口[0 65536]=>131074测试中的65536匹配
无论你使用哪种语言,几乎肯定都有更好的方法来解析其库中的URL。我认为你不需要嵌套的paren。这是一种蛮力,但我认为它会起作用。事实上,我可以否认你的能力。即使正则表达式可以工作(我没有理由怀疑它可以工作),但像它这样的可怕的、不可读的怪物给正则表达式起了一个坏名字。例如,TimP提供了一个更具可读性的解决方案,但我的建议是:如果你想解析URL,请使用解析器。然后您可以只允许正则表达式
^\d{1,5}$
(或者^0*\d{1,5}$
,如果您想允许前导零),并检查以确保在词法阶段后它小于64K。这是一种证明,因为我可以这样做吗?“如果你唯一的工具是锤子,那么把一切都当作钉子来对待是很有诱惑力的。”——仪器定律+1但除了用每个有效字符串进行验证外,OP还应该测试无效字符串,尤其是边界值。当他这么做的时候,保持低调也不会有什么坏处。@Lieven:哦,绝对的,无效的字符串绝对应该被测试。这是我观点的一部分——他需要弄清楚“0000”是否应该无效。将进行编辑以使其更清晰。这当然更具可读性,因此,尽管我怀疑问题的合理性:-),+1是一个好答案。我想知道是否需要重复6553,655,65,6。@ceving:你是对的,没有必要。请看我的编辑。@TimPietzcker将固定数字移到开头是否更快<代码>6(?:5(?:5(?:3[0-5]|[0-2]\d)|[0-4]\d{2})|[0-4]\d{3})BTW:0是保留端口,不能用于连接。这意味着它将接受它作为一个端口号……尽管有时[0-9]
比\d
更好,例如在.NET中,d
也将匹配一些您可能不希望匹配的数字,如۳
(印度-阿拉伯语3)。不错,但是您需要在顶层使用另一组括号才能使锚点正常工作:^(foo | bar)$
,而不是^foo | bar$
第一个regexp被破坏。第二个也是。p
/^(6553[0-5])|(655[0-2]\d)|(65[0-4]\d{2})|(6[0-4]\d{3})|([1-5]\d{4})|([1-9]\d{1,3})|(\d)$/
\d{1,5}
6(?:[0-4]\d{3}|5(?:[0-4]\d{2}|5(?:[0-2]\d|3[0-5])))|(?:[1-5]\d{0,3}|[6-9]\d{0,2})?\d
#! /usr/bin/perl
use strict;
use warnings;
my $port = qr{
6(?:[0-4]\d{3}|5(?:[0-4]\d{2}|5(?:[0-2]\d|3[0-5])))|(?:[1-5]\d{0,3}|[6-9]\d{0,2})?\d
}x;
sub test {
my ($label, $regexp, $start, $stop) = @_;
my $matches = 0;
my $tests = 0;
foreach my $n ($start..$stop) {
$tests++;
$matches++ if "$n" =~ /^$regexp$/;
$tests++;
$matches++ if "0$n" =~ /^$regexp$/;
}
print "$label [$start $stop] => $matches matches in $tests tests\n";
}
test "Port", $port, 0, 2**16;
Port [0 65536] => 65536 matches in 131074 tests