任何人看到我的正则表达式端口号有什么问题?

asd*_*das 8 regex

我为端口号做了一个正则表达式(在你说这是一个坏主意之前,它进入了一个更大的URL的URL,这比听起来要困难得多).

我的同事说这真的很糟糕,并不会抓住一切.我不同意.

我相信这个东西可以捕获从0到65535的所有内容,而不是别的,我正在寻找对此的确认.

单行版(适用于计算机):

/(^[0-9]$)|(^[0-9][0-9]$)|(^[0-9][0-9][0-9]$)|(^[0-9][0-9][0-9][0-9]$)|((^[0-5][0-9][0-9][0-9][0-9]$)|(^6[0-4][0-9][0-9][0-9]$)|(^65[0-4][0-9][0-9]$)|(^655[0-2][0-9]$)|(^6553[0-5]$))/
Run Code Online (Sandbox Code Playgroud)

人类可读版本:

/(^[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)
Run Code Online (Sandbox Code Playgroud)

有人可以证实我的理解是正确的(或其他)吗?

Tim*_*ker 26

你可以大大缩短它:

^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])$
Run Code Online (Sandbox Code Playgroud)
  • 无需每次都重复锚点
  • 不需要很多捕获组
  • 无需拼写重复.

0*如果您不想允许前导零,请删除前导.

这个正则表达式也更好,因为它首先匹配特殊情况(65535,65001等),从而避免一些回溯.

哦,既然你说你想用这个作为URL的正则表达式较大的一部分,则应更换这两个^$\b(字边界锚).


编辑: @ceving问是否重复6553,655,656确有必要.答案是否定的 - 您也可以使用嵌套的正则表达式,而不必重复那些前导数字.我们只考虑一下这一部分

6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}
Run Code Online (Sandbox Code Playgroud)

这可以改写为

6(?:[0-4][0-9]{3}|5(?:[0-4][0-9]{2}|5(?:[0-2][0-9]|3[0-5])))
Run Code Online (Sandbox Code Playgroud)

我认为这使得正则表达式的可读性比现在更低.详细模式使差异更清晰.相比

6553[0-5]       |
655[0-2][0-9]   |
65[0-4][0-9]{2} |
6[0-4][0-9]{3}  
Run Code Online (Sandbox Code Playgroud)

6 
(?:
 [0-4][0-9]{3}
|
 5
 (?:
  [0-4][0-9]{2}
 |
  5
  (?:
   [0-2][0-9]
  |
   3[0-5]
  )
 )
)
Run Code Online (Sandbox Code Playgroud)

一些性能测量:针对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))
Run Code Online (Sandbox Code Playgroud)

输出:

7.7265428834649
7.556472630353351
Run Code Online (Sandbox Code Playgroud)


gpe*_*che 8

就个人而言,我只会匹配一个数字,然后我会用代码检查数字是否在范围内.


Jon*_*eet 6

好吧,很容易证明它将验证任何正确的端口:只生成每个有效的字符串并测试它通过.确保它不允许任何不应该的东西更难 - 显然你不能绝对测试每个无效的字符串.你绝对应该测试简单的情况和你认为可能错误传递的任何东西(或者使用较小的正则表达式错误传递的东西- "65536"就是一个例子).

它会允许一些略微奇怪的端口规格 - 例如"0000".你允许领先零吗?

您可能还需要考虑是否确实需要为每种情况单独指定^和$,或者是否可以使用^(case 1)|(case 2)|...$.哦,量词也可以简化"1到4位"的情况:([0-9]{1,4})将找到1到4位数字.

(顺便说一句,你可能想要听起来有点不那么傲慢.如果你和其他人一起工作,以较不激进的方式进行沟通可能会比仅仅证明你的正则表达是正确的更能改善每个人的日子. .)