【问题标题】:Non-greedy newline character match with re.DOTALL与 re.DOTALL 的非贪婪换行符匹配
【发布时间】:2016-01-19 10:51:11
【问题描述】:

我正在尝试在 Python 中重现此正则表达式:https://regex101.com/r/tP1bS4/2

entry 的样本数据如下:

[ 10.0.7.58/54648 -> 31.221.26.40/80 (http response) ]-
|
| server   = 31.221.26.40/80
| app      = ???
| lang     = none
| params   = anonymous
| raw_sig  = 1:Content-Type,?Last-Modified,?Cache-Control,?Expires,Date,Connection=[keep-alive]:Keep-Alive,Accept-Ranges:
|

基本上我想得到一组包含两个元素的组:服务器 IP 和端口,从第三行开始。

这是我的 Python 尝试;

server = re.findall( '\| server[ \s]+= (.*)\/(.*)\n', entry, re.DOTALL)
print server

这给了我这个server 组:

[('31.221.26.40', '80\n| app      = ???\n| lang     = none\n| params   = anonymous\n| raw_sig  = 1:Content-Type,?Last-Modified,?Cache-Control,?Expires,Date,Connection=[keep-alive]:Keep-Alive,Accept-Ranges:\n|')]

如您所见,\n 上没有拆分。预期结果是:

[('31.221.26.40', '80')]

【问题讨论】:

  • 如果您删除 DOTALL,它应该可以按预期工作。
  • @WiktorStribiżew 谢谢,它确实有效,但为什么会这样?
  • 因为带有 DOTALL 的 . 匹配换行符。无论如何,您的模式效率低下。应该是server = re.findall(r'\| server\s+= ([^/]*)/(\d+)', entry)
  • @WiktorStribiżew 谢谢!我实际上已经用\| server[ \s]+= (.*)\/(.*?)\n 解决了这个问题,现在我知道了,尽管在这种情况下你的也可以。
  • 那个模式又很糟糕……不过你觉得够用就可以用。

标签: python regex


【解决方案1】:

让我澄清一些事情。 re.DOTALL 修饰符使 . 符号匹配 any 符号,包括换行符。带有 DOTALL 的 .* 子模式匹配字符串直到结尾。

如果你使用'\| server[ \s]+= (.*)\/(.*)\n',第一个.*匹配到最后一个/,第二个.*匹配到最后一个\n,因为它是贪婪的(也就是说,引擎会抓取所有的字符串的其余部分,然后回溯以适应后续子模式)。

由于您的数据位于 1 行,您想让. 匹配换行符。因此,解决问题的最简单方法是使用

server = re.findall(r'\|\s*server\s*=\s*(.*?)/(.*)', entry)

regex demo

但是,点匹配只有在我们不知道会出现什么字符时才有用。您可以使用字符类增强正则表达式:

server = re.findall(r'\|\s*server\s*=\s*([^\s/]+)/(\d+)', entry)

另一个regex demo(注意效率提高了x2倍)

([^\s/]+) 匹配除空格以外的 1 个或多个字符,/\d+ 匹配 1 个或多个数字。

【讨论】:

  • 太棒了!- 认识你需要时间,因为你有了新名字!
  • @SIslam:也一样,只是另一种音译方案+名字。
【解决方案2】:

我会尝试-

re.findall(r'\| server[ \s]+= (.*)\/([^\/\W]*)',string)

Demo

空间标准化\|\s*server\s*=\s*([^/]*?)/([^/\W]*)更健壮

【讨论】:

    【解决方案3】:

    没有正则表达式:

    import io
    
    data = '''[ 10.0.7.58/54648 -> 31.221.26.40/80 (http response) ]-
    |
    | server   = 31.221.26.40/80
    | app      = ???
    | lang     = none
    | params   = anonymous
    | raw_sig  = 1:Content-Type,?Last-Modified,?Cache-Control,?Expires,Date,Connection=[keep-alive]:Keep-Alive,Accept-Ranges:
    |'''
    
    
    for line in io.StringIO(data):
        if line.startswith('| server '):
            print(line[13:].rstrip().split('/'))
    

    如果你不知道“服务器”后面的空格或制表符的确切数量,可以这样更改:

    if line.startswith('| server'):
        print(line.split()[3].split('/'))
    

    【讨论】:

      猜你喜欢
      • 2017-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-11
      • 2013-06-09
      • 2012-07-16
      • 2011-12-10
      • 1970-01-01
      相关资源
      最近更新 更多