RegEx:删除可包含其他句点的字符串中的最后一个句点(挖掘输出)

时间:2014-10-21 22:59:55

标签: python regex find

我正在尝试解析linux dig命令的输出,并使用正则表达式一次性执行多项操作。

假设我挖掘主持人mail.yahoo.com

/usr/bin/dig +nocomments +noquestion \
    +noauthority +noadditional +nostats +nocmd \
    mail.yahoo.com A

此命令输出:

mail.yahoo.com.                   0  IN  CNAME  login.yahoo.com.
login.yahoo.com.                  0  IN  CNAME  ats.login.lgg1.b.yahoo.com.
ats.login.lgg1.b.yahoo.com.       0  IN  CNAME  ats.member.g02.yahoodns.net.
ats.member.g02.yahoodns.net.      0  IN  CNAME  any-ats.member.a02.yahoodns.net.
any-ats.member.a02.yahoodns.net. 12  IN  A      98.139.21.169

我想要的是找到所有<host><record_type><resolved_name>部分而不使用最后一个句点只使用一个正则表达式

对于mail.yahoo.com的这个特定示例,它是:

[
    ('mail.yahoo.com', 'CNAME', 'login.yahoo.com'),
    ('login.yahoo.com', 'CNAME', 'ats.login.lgg1.b.yahoo.com'),
    ('ats.login.lgg1.b.yahoo.com', 'CNAME', 'ats.member.g02.yahoodns.net'),
    ('ats.member.g02.yahoodns.net', 'CNAME', 'any-ats.member.a02.yahoodns.net'),
    ('any-ats.member.a02.yahoodns.net', 'A', '98.139.21.169'),
]

但事实证明,dig命令可能会在名称末尾显示句点:

    mail.yahoo.com. 
        ^     ^   ^
        |     |   |
  Good dot    |   |
              |   |
        Good dot  |
                  |
           (!) Baaaad dot

使用正则表达式来分割dig的输出并返回最终句点的名称是非常简单的:

regex = re.compile("^(\S+).+IN\s+([A-Z]+)\s+(\S+)\.*\s*$",re.MULTILINE)

但是使用该正则表达式调用.findall会返回主机中的最后一段时间,因为\S+也会匹配最后一段时间:

[
    ('mail.yahoo.com.', 'CNAME', 'login.yahoo.com.'),
    ('login.yahoo.com.', 'CNAME', 'ats.login.lgg1.b.yahoo.com.'),
    ('ats.login.lgg1.b.yahoo.com.', 'CNAME', 'ats.member.g02.yahoodns.net.'),
    ('ats.member.g02.yahoodns.net.', 'CNAME', 'any-ats.member.a02.yahoodns.net.'),
    ('any-ats.member.a02.yahoodns.net.', 'A', '98.139.21.169'),
]

所以我需要某些匹配所有非空格\S,除非它是一个句点后面跟一个空格。

我做了无数尝试,但我还没有找到合适的解决方案。

提前谢谢!

PS: 我知道我总是可以使用“简单”的正则表达式,并且(在第二次传递时)删除找到的字符串的最后一个点,但我很好奇这是否可以一次性使用正则表达式来完成。

4 个答案:

答案 0 :(得分:2)

  

但是使用该正则表达式调用.findall确实会返回主机中的最后一段时间,因为\S+也会匹配上一个句点...

这里有两个问题。

首先,一旦你用反斜杠转义东西,你需要使用原始字符串文字(r"…"),或者你也必须逃避反斜杠。我真的不确定你的反斜杠前缀字符是否碰巧匹配Python反斜杠转义序列,但这本身就足以成为使用原始字符串文字的原因,所以你的读者不必查看确切的规则。

其次,这个问题的一般情况是默认情况下正则表达式重复是greedy:它们将尽可能匹配,同时仍然允许模式的其余部分匹配;如果您希望它们尽可能匹配 little ,同时仍允许模式的其余部分匹配,则需要在?或{{1}之后添加+ }。

在您的特定情况下,*可以匹配所有内容,包括最终\S+.将成功匹配0 \.*\s* s和0空格。但是.将为模式的下一部分留下最后的\S+?。您还可以通过在其后添加句点来强制超出第一组的时间段。像这样:

.

Regular expression visualization

Debuggex Demo

答案 1 :(得分:1)

你可以简单地强迫你的小组结尾没有句号(并且它不包含空格):

npg = '([^\.\s]+(?:.[^\.\s]+)*)' #not_period_ending_group
regex = re.compile("^" + npg + ".+IN\s+([A-Z]+)\s+" + npg +".+$",re.MULTILINE)

答案 2 :(得分:1)

您可以将此模式与多线修改器一起使用:

^([^ ]+)(?<!\.)\.?[ ]+[0-9]+[ ]+IN[ ]+([^ ]+)[ ]+(.+(?<!\.))\.?$

以$ 1 $ 2和$ 3

的形式存储的群组

DEMO

编辑:试试这个:

^([^ \t]+)(?<!\.)\.?[ \t]+[0-9]+[ \t]+IN[ \t]+([^ \t]+)[ \t]+(.+(?<!\.))\.?$

答案 3 :(得分:0)

作为替代答案,我建议使用str.split(), 如果你的字符串行在L这样的列表中,你需要这个:

[(line[0][:-1],line[3],line[4][:-1]) for line in L]

请注意[:-1]从主机地址中删除最后一个.