重叠/混合行的正则表达式

REGEX for overlapped/intermingled rows

提问人:Gareth Ward 提问时间:6/9/2021 最后编辑:Gareth Ward 更新时间:6/10/2021 访问量:56

问:

我有一个非常烦人的问题,这是由于狡猾的PDF数字化造成的。

无论如何,理想情况下,具有不同列的一系列行将如下所示:

Code  Cost  Quantity
ABC  45.00  4
FED  60.00  5
GHK  30.00  5

使用正则表达式,可以很容易地将它们拆分为行,然后获取每个单独的列。

但是,我发现一个特别烦人的文本总是像这样出现:

Code  Cost  Quantity
ABC FED GHK   45.00 60.00 30.00  4 5 5

我一辈子都无法弄清楚如何让正则表达式分离出这些重叠的行中的每一个,就像第一个例子一样。积极的展望可以让我有所帮助,但通常发生的事情是,我得到 ABC 45.00 4,然后得到 FED 45.00 4,我建立的展望不会遍历所有单独的列。

我怀疑我可以使用命名模式或其他东西,与第一组匹配:

(?>(?<match1>((?>\s|\b)\w{3}\s).+\s+\s(\d+\.\d{2})\s.*\s+\s(\d{1})\s.*))

然后以某种方式重用该捕获组,对其进行迭代。

坚持积极的展望只会迭代第一组,所以我显然在做一些愚蠢的事情:

https://regex101.com/r/Uxx8bZ/1

从理论上讲,我可以以其他方式分隔行(例如,对于每个大空间,这是一列),但似乎这应该是可能的。

帮助感谢!

正则表达式 多列 重叠匹配

评论


答:

1赞 The fourth bird 6/9/2021 #1

在示例数据中,随附的数据是 3 倍的空格字符,然后是右侧的非空格字符。

如果该结构始终相同,则可以捕获大写字符,并捕获 lookahead 断言中的其他 2 个字段。

([A-Z]+)(?=\s+\S+\s+\S+\s+(\d+(?:\.\d+)?)\s+\S+\s+\S+\s+(\d+))
  • ([A-Z]+)捕获组 1 中 1+ 次字符 A-Z
  • (?=积极展望未来,向右主张
    • \s+\S+\s+\S+\s+(\d+(?:\.\d+)?)在 3 个字段捕获 1+ 位数字后,组 2 中有一个可选的小数部分
    • \s+\S+\s+\S+\s+(\d+)在 3 个字段之后,捕获组 1 中的 3+ 位数字
  • )近距离展望

观看正则表达式演示

使用 re.findall 重新绑定捕获组值的示例:

import re

pattern = r"([A-Z]+)(?=\s+\S+\s+\S+\s+(\d+(?:\.\d+)?)\s+\S+\s+\S+\s+(\d+))"
s = r"ABC FED GHK   45.00 60.00 30.00  4 5 5"
print(re.findall(pattern, s))

输出

[('ABC', '45.00', '4'), ('FED', '60.00', '5'), ('GHK', '30.00', '5')]

评论

0赞 Gareth Ward 6/9/2021
非常感谢您的回复 - 这看起来可能是一条向下的道路。唯一的问题是,我不知道有多少行,可能是 1 行,也可能是 20 行。有没有一种简单的方法可以更改您的代码来解决这个问题?我尝试了这个可怕的事情,但我还没有完全明白:([A-Z]+)(?=(?:\s+\S+)+(\S+)(?:\s+\S+)+(\S+))
0赞 The fourth bird 6/9/2021
@GarethWard 行是什么意思?你的意思是现在你有 3 个,可能有 20 个部分。在这种情况下,您可以使用量词 regex101.com/r/ArU5GE/1 请注意,此方法仅在列值为单个“单词”时才有效([A-Z]+)(?=(?:\s+\S+){3}\s*(\d+(?:\.\d+)?)(?:\s+\S+){3}\s+(\d+))
0赞 Gareth Ward 6/9/2021
再次感谢,我对此感到兴奋!通过行,我的意思是在原始模式中,有 3 个重叠的“组”,即 3 行,[('ABC', '45.00', '4'), ('FED', '60.00', '5'), ('GHK', '30.00', '5')]。可能只有一个,也可能是 20 个,例如:[('ABC', '45.) 00', '4'), ('FED', '60.00', '5'), ('港怡', '30.00', '5'), ('港合港澳大港', '30.00', '5'), ('港合港澳大湾区', '30.00', '5'), ('港合港澳大湾区', '30.00', '5'), ('港怡', '30.00', '5'), ('港怡', '30.00', '5')] 所以 5 个“行”看起来像 ABC ABC ABC ABC ABC ABC ABC 如果你明白我!
0赞 The fourth bird 6/10/2021
@GarethWard 是的,像这样 regex101.com/r/9Cn9T6/1([A-Za-z]+)(?=(?:\s+\S+){4}\s*(\d+(?:\.\d+)?)(?:\s+\S+){4}\s+(\d+))
0赞 Gareth Ward 6/10/2021
因此,如果我知道在执行 REGEX 之前有多少行,我可以看到如何更改它(这应该很容易解决,所以这应该能让我到达我想去的地方!我猜在不知道会有多少行的情况下让它工作会是一个太大的要求,即一个单一的正则表达式模式,它同时适用于包含 3 行、4 行、5 行等的字符串。例如,如果我输入一个 {1,},那只会破坏它,因为它尽可能多地匹配。感谢您的输入!