1

我正在尝试进行非贪婪的否定匹配,我也需要捕获它。我在 Python 中使用这些标志,re.DOTALL | 重新本地化 | re.MULTILINE,对一些文本文件“数据库”进行多行清理,其中每个字段都以反斜杠开始新行。每条记录都以 \lx 字段开头。

\lx foo
\ps n
\nt note 1
\ps v
\nt note
\ge happy
\nt note 2
\ge lonely
\nt note 3
\ge lonely
\dt 19/Dec/2011

\lx bar
...

我试图确保每个 \ge 字段在其记录中的某个位置上方都有一个 \ps 字段,一对一。目前,一个 \ps 后面经常跟几个 \ge ,因此需要复制下来,就像上面的两个单独的 \ge 一样。

以下是大部分需要的逻辑:在任何 \ps 字段之后,但在遇到另一个 \ps 或 \lx 之前,先找到一个 \ge,然后再找到另一个 \ge。捕获所有内容,以便可以将 \ps 字段复制到第二个 \ge 之前。

这是我的非功能性尝试。替换这个:

^(\\ps\b.*?\n)((?!^\\(ps|lx)*?)^(\\ge.*?\n)((?!^\\ps)*?)^(\\ge.*?\n)

有了这个:

\1\2\3\4\1\5

即使在一个小文件(34 行长)上,我也会遇到内存错误。当然,即使这可行,我也必须多次运行它,因为它只是试图处理第二个 \ge,而不是第三个或第四个。所以这方面的任何想法也会让我感兴趣。

更新: Alan Moore 的解决方案效果很好,尽管有些情况需要稍作调整。可悲的是,我不得不关闭 DOTALL,否则我无法阻止第一个 .* 包括随后的 \ps 字段——即使是非贪婪的 .*? 形式。但是我很高兴刚才在正则表达式点信息中了解了 (?s) 修饰符。这使我可以在一般情况下关闭 DOTALL,但仍可在其他必要的正则表达式中使用它。

这是建议的正则表达式,浓缩为我需要的单行格式:

^(?P<PS_BLOCK>(?P<PS_LINE>\\ps.*\n)(?:(?!\\(?:ps|lx|ge)).*\n)*\\ge.*\n)(?P<GE_BLOCK>(?:(?!\\(?:ps|lx|ge)).*\n)*\\ge.*\n)

那行得通,但是当我修改上面的示例时,它在“注释 2”上方插入了 \ps。它还将 \lxs 和 \ge2 与 \lx 和 \ge 相同(需要一些 \b)。所以,我选择了一个稍微调整的版本:

^(?P<PS_BLOCK>(?P<PS_LINE>\\ps\b.*\n)(?:(?!\\(?:ps|lx|ge)\b).*\n)*\\ge\b.*\n)(?P<AFTER_GE1>(?:(?!\\(?:ps|lx|ge)\b).*\n)*)(?P<GE2_LINE>\\ge\b.*\n)

这个替换字符串:

\g<PS_BLOCK>\g<AFTER_GE1>\g<PS_LINE>\g<GE2_LINE>

再次感谢!

4

2 回答 2

1

每当您遇到正则表达式问题并告诉自己“我必须多次运行它,..”时,这清楚地表明您需要编写一个解析器:-)

该语言似乎很常规,因此解析器应该很容易编写,也许从以下简单的东西开始:

def parse_line(line):
    kind, value = line.split(' ', 1)  # split on the first space
    kind = kind[1:]                   # remove the \
    parsed_value = globals().get('parse_' + kind, lambda x:x)(value)
    return (kind, parsed_value)

def parse_dt(value):
    val = ... # create datetime.date() from "19/Dec/2011"
    return val

globals()用它来编写状态机可能有点太可爱了,但它节省了大量的样板代码...... :-)

将您的输入转换为元组列表:

records = [parse_line(line) for line in open("myfile.dta")]

弄清楚一个("ps", ..)元组之前是否总是有一个元("ge", ..)组应该很容易——例如,首先注意所有lx元组的位置......

于 2012-08-12T22:26:15.330 回答
1

您收到内存错误是因为您使用了该DOTALL标志。如果您的数据确实按照您显示的方式进行了格式化,那么您无论如何都不需要该标志;默认行为正是您想要的。您也不需要非贪婪修饰符 ( ?)。

试试这个正则表达式:

prog = re.compile(r"""
    ^
    (?P<PS_BLOCK>
      (?P<PS_LINE>\\ps.*\n)
      (?:                   # Zero or more lines that
        (?!\\(?:ps|lx|ge))  # don't start with
        .*\n                # '\ps', '\lx', or '\ge'...
      )*
      \\ge.*\n              # ...followed by a '\ge' line.
    )
    (?P<GE_BLOCK>
      (?:                   # Again, zero or more lines
        (?!\\(?:ps|lx|ge))  # that don't start with
        .*\n                # '\ps', '\lx', or '\ge'...
      )*
      \\ge.*\n              # ...followed by a '\ge' line.
    )
    """, re.MULTILINE | re.VERBOSE)

替换字符串将是:

r'\g<PS_BLOCK>\g<PS_LINE>\g<GE_BLOCK>'

你仍然需要做多次传球。如果 Python 支持\G,则没有必要,但您可以使用subn并检查number_of_subs_made返回值。

于 2012-08-12T23:52:03.883 回答