正则表达式向前查看和向后查看

5

我有以下两种抓取数据的变体:

   txt =  '''Käuferprovision: 3 % zzgl. gesetzl. MwSt.''' # variation 1

    txt = '''Käuferprovision: Die Courtage i.H.v. % 3,57 inkl. MwSt. ist''' # variation 2

我希望您能够使用一个正则表达式来获取浮点数百分比,所以在第一个例子中为3.0,在第二个例子中为3.57。
我目前尝试过以下内容:
m = re.search(r'.{3}.%.{5}',txt)
txt = m.group().split("%")[1:]
txt = ("".join(txt)).replace(",",".")
print(txt)

这对变异2起作用,但对变异1无效。

5个回答

3
你可以使用带有2个捕获组的交替,然后检查哪个组存在。
\b(\d+(?:\,\d+)?)\s*%|%\s*(\d+(?:\,\d+)?)\b

查看正则表达式演示

该表达式匹配:

  • \b 单词边界
  • (\d+(?:\,\d+)?)\s*% 捕获第1组 - 匹配带有可选小数、可选空格字符和%的数字
  • | 或者
  • %\s*(\d+(?:\,\d+)?) 捕获第2组 - \b 单词边界 - 与第1组相反
  • \b 单词边界

例如:

import re

regex = r"\b(\d+(?:\,\d+)?)\s*%|%\s*(\d+(?:\,\d+)?)\b"
test_str = ("Käuferprovision: 3 % zzgl. gesetzl. MwSt.\n"
            "Käuferprovision: Die Courtage i.H.v. % 3,57 inkl. MwSt. ist")

matches = re.finditer(regex, test_str, re.MULTILINE)
for matchNum, match in enumerate(matches, start=1):
    if match.group(1):
        print(match.group(1).replace(',', '.'))
    else:
        print(match.group(2).replace(',', '.'))

输出

3
3.57

如果百分号之间的空格是固定的,你也可以使用预测后缀来匹配字符串,而不需要捕获组。
(?<=% )\b\d+(?:,\d+)\b|\b\d+(?:,\d+)?(?= %)

查看另一个正则表达式演示

示例

import re

pattern = r"(?<=% )\b\d+(?:,\d+)\b|\b\d+(?:,\d+)?(?= %)"
test_str = ("Käuferprovision: 3 % zzgl. gesetzl. MwSt.\n"
            "Käuferprovision: Die Courtage i.H.v. % 3,57 inkl. MwSt. ist")

for s in re.findall(pattern, test_str):
    print(s.replace(",", "."))

输出

3
3.57

1
@Jan,使用re是行不通的,你需要安装PyPi包。但是这是个好主意 :-) - The fourth bird

3

还有一种方法 - 使用带有PyPi regex的分支重置:

import regex as re

data = """
Käuferprovision: 3 % zzgl. gesetzl. MwSt.
Käuferprovision: Die Courtage i.H.v. % 3,57 inkl. MwSt. ist
"""
rx = re.compile(r'(?|(?P<value>\d+(?:,\d+)?)\s*%|%\s*(?P<value>\d+(?:,\d+)?))')

for m in rx.finditer(data):
    print(float(m.group('value').replace(',', '.')))

这将产生

3.0
3.57

regex101.com上看到演示


如果你想要完全疯狂,可以使用子程序分支重置(这显然有点过头了):

(?(DEFINE)
    (?<value>\d+(?:,\d+)?)
    (?<before>%\s+)
    (?<after>\s+%)
)

(?|(?P<mwst>(?&value))(?&after)|(?&before)(?P<mwst>(?&value)))

regex101.com 上查看其他演示。


2
那是艺术 :-) - The fourth bird
2
@Thefourthbird:谢谢,伙计。有时候我觉得用正则表达式就像画画一样,真的。但是很少有人真正看到它们的美丽。 - Jan

3

您可以尝试以下代码来获取百分比值并将其转换为float

>>> import re
>>> arr = ['Käuferprovision: 3 % zzgl. gesetzl. MwSt.', 'Käuferprovision: Die Courtage i.H.v. % 3,57 inkl. MwSt. ist']
>>> rx = re.compile(r'\d+(?:[.,]\d+)*(?=\s*%)|(?<=%)\s*\d+(?:[.,]\d+)*')
>>> for s in arr:
...     for m in rx.finditer(s): print (float(m.group().replace(',', '.')))
...
3.0
3.57

正则表达式演示

在线代码演示


1
使用浮点数可以正确处理前导和尾随空格吗?那真聪明,这是值得记住的事情 :-) - The fourth bird

2

我建议在正则表达式中使用\d来匹配数字字符。?也可以帮助你匹配一个或零个实例。下面的正则表达式可以同时匹配这两个字符串:

re.search(r'([\d,]+)? % ([\d,]+)?',txt)

[\d,] 简单地匹配数字字符或字符 ,[\d,]+ 匹配一个或多个这些字符。如果存在这些字符,则 ([\d,]+)? 将匹配它们,否则它将不尝试捕获它们。


1
我的两分钱:
(%?\s*(\d+(?:,\d+)?)\s*%?)

请查看在线演示

  • (%?\s* - 打开第一个捕获组,其中包含可选的百分比符号和0个或多个空格字符;
    • (\d+(?:,\d+)?) - 第二个捕获组持有您的数值。
  • \s*%?) - 在0个或多个空格字符后跟可选的百分比符号后关闭第一个捕获组。

现在你可以尝试:

import re

data = """
Käuferprovision: 3 % zzgl. gesetzl. MwSt. 5,44
Käuferprovision: Die Courtage i.H.v. % 3,57 inkl. MwSt. ist
"""

for i in re.findall(r'(%?\s*(\d+(?:,\d+)?)\s*%?)', data):
    if '%' in i[0]:
      print(float(i[1].replace(',', '.')))

输出:

3.0
3.57

我相信你可以将上述内容压缩为列表推导式。
import re

data = """
Käuferprovision: 3 % zzgl. gesetzl. MwSt. 5,44
Käuferprovision: Die Courtage i.H.v. % 3,57 inkl. MwSt. ist
"""

print([float(i[1].replace(',', '.')) for i in re.findall(r'(%?\s*(\d+(?:,\d+)?)\s*%?)', data) if '%' in i[0]])

输出:

[3.0, 3.57]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接