【问题标题】:Is there a way to check for two different patterns in the same string in regex python?有没有办法在正则表达式 python 中检查同一字符串中的两个不同模式?
【发布时间】:2021-05-16 02:39:30
【问题描述】:

我想从字符串中提取某些数字。问题是字符串可以包含两种不同模式的数字。如何在 re.search 中创建正则表达式模式,以便我可以同时使用两种模式在单个字符串中进行搜索?

例如,

## extract 65.45 from this string
string = '1112 (65.45%)'

所以,如果我执行以下操作,它会起作用

re.search('.*?\((.*)%\)', string).group(1)

我得到了预期的结果65.45

现在,我需要在同一文本中查找另一种字符串。

## from this string, extract 4.00 which appears before [
string = '4.00 [3.00 - 4.50]'

re.search('^(\S+)\s\[.*', string).group(1)

给了我想要的结果:4.00

但是如果我像下面这样组合它们,它只会提取第一个匹配的。

re.search('^(\S+)\s\[.*|.*?\((.*)%\)', string).group(1)

在这种情况下,只有包含方括号的字符串才会提取值,如果字符串有 % 符号则不会。我该如何解决这个问题?

例如,如果我有如下字符串列表:

['73 (1.40%)', '38 (1.55%)', '27 (2.17%)', '32 (1.46%)', '10 (1.46%)', '11 (1.04%)', '11 (1.41%)', '7 (1.34%)', '4 (1.24%)', '28 (1.27%)', '750 (14.41%)', '381 (15.54%)', '182 (14.60%)', '313 (14.27%)', '4.10 [3.73 - 4.45]', '4.08 [3.70 - 4.42]', '4.13 [3.77 - 4.47]', '4.13 [3.78 - 4.47]', '4.07 [3.70 - 4.42]', '4.07 [3.70 - 4.43]', '4.07 [3.70 - 4.40]', '4.09 [3.73 - 4.42]', '4.03 [3.63 - 4.40]', '4.10 [3.70 - 4.47]']

我想对提取的每个值执行某些操作并与特定阈值进行比较。

使用 for 循环,我做了这样的事情:

for val in string: 
    match = re.search('^(\S+)\s\[.*|.*?\((.*)%\)', val)
    print(match)

结果如下:

<re.Match object; span=(0, 10), match='73 (1.40%)'>
<re.Match object; span=(0, 10), match='38 (1.55%)'>
<re.Match object; span=(0, 10), match='27 (2.17%)'>
<re.Match object; span=(0, 10), match='32 (1.46%)'>
<re.Match object; span=(0, 10), match='10 (1.46%)'>
<re.Match object; span=(0, 10), match='11 (1.04%)'>
<re.Match object; span=(0, 10), match='11 (1.41%)'>
<re.Match object; span=(0, 9), match='7 (1.34%)'>
<re.Match object; span=(0, 9), match='4 (1.24%)'>
<re.Match object; span=(0, 10), match='28 (1.27%)'>
<re.Match object; span=(0, 12), match='750 (14.41%)'>
<re.Match object; span=(0, 12), match='381 (15.54%)'>
<re.Match object; span=(0, 12), match='182 (14.60%)'>
<re.Match object; span=(0, 12), match='313 (14.27%)'>
<re.Match object; span=(0, 18), match='4.10 [3.73 - 4.45]'>
<re.Match object; span=(0, 18), match='4.08 [3.70 - 4.42]'>
<re.Match object; span=(0, 18), match='4.13 [3.77 - 4.47]'>
<re.Match object; span=(0, 18), match='4.13 [3.78 - 4.47]'>
<re.Match object; span=(0, 18), match='4.07 [3.70 - 4.42]'>
<re.Match object; span=(0, 18), match='4.07 [3.70 - 4.43]'>
<re.Match object; span=(0, 18), match='4.07 [3.70 - 4.40]'>
<re.Match object; span=(0, 18), match='4.09 [3.73 - 4.42]'>
<re.Match object; span=(0, 18), match='4.03 [3.63 - 4.40]'>
<re.Match object; span=(0, 18), match='4.10 [3.70 - 4.47]'>

但不确定如何提取确切的值。

我必须执行 .group() 来提取值,但这需要我知道确切的位置。我正在努力弄清楚如何做到这一点。

如果我做match.group(2),那么我会得到以下结果:

1.40
1.55
2.17
1.46
1.46
1.04
1.41
1.34
1.24
1.27
14.41
15.54
14.60
14.27
None
None
None
None
None
None
None
None
None
None

【问题讨论】:

  • 您能否包含一个 single 示例输入,其中包含您要匹配的 both 版本以及捕获的值?
  • @TimBiegeleisen,我刚刚添加了一个我要检查的值列表。这是你要求的吗?

标签: python regex


【解决方案1】:

我将只使用一个简单的正则表达式列表,并为我想要测试的每个字符串遍历它们。将使用第一个命中的正则表达式。我还将预先编译正则表达式以节省 CPU 周期。这更易于遵循可读性并且易于添加新模式:

import re

regexs = [
    re.compile(r".*?\((.*)%\)"), 
    re.compile(r"^(\S+)\s\[.*"),
]

data = [
    "73 (1.40%)",
    "38 (1.55%)",
    "27 (2.17%)",
    "750 (14.41%)",
    "381 (15.54%)",
    "4.10 [3.73 - 4.45]",
    "4.08 [3.70 - 4.42]",
    "4.13 [3.77 - 4.47]",
    "this shouldn't match"
]


for val in data:
    for regex in regexs:
        if match := regex.search(val):
            print("Matched: " + match.group(1))
            break
    else:
        print("No match: " + val)

输出:

Matched: 1.40
Matched: 1.55
Matched: 2.17
Matched: 14.41
Matched: 15.54
Matched: 4.10
Matched: 4.08
Matched: 4.13
No match: this shouldn't match

【讨论】:

  • 我真的很喜欢这个解决方案,因为它不需要我更改正则表达式,而且如果需要,我还可以在以后添加其他正则表达式。
【解决方案2】:

这是一种适用于您的确切输入的方法,其中每个列表条目始终具有两种匹配模式之一:

inp = ['73 (1.40%)', '38 (1.55%)', '27 (2.17%)', '32 (1.46%)', '10 (1.46%)', '11 (1.04%)', '11 (1.41%)', '7 (1.34%)', '4 (1.24%)', '28 (1.27%)', '750 (14.41%)', '381 (15.54%)', '182 (14.60%)', '313 (14.27%)', '4.10 [3.73 - 4.45]', '4.08 [3.70 - 4.42]', '4.13 [3.77 - 4.47]', '4.13 [3.78 - 4.47]', '4.07 [3.70 - 4.42]', '4.07 [3.70 - 4.43]', '4.07 [3.70 - 4.40]', '4.09 [3.73 - 4.42]', '4.03 [3.63 - 4.40]', '4.10 [3.70 - 4.47]']
matches = [re.findall(r'\b\d+ \((\d+(?:\.\d+)?%)\)|(\d+(?:\.\d+)?) \[\d+(?:\.\d+)? - \d+(?:\.\d+)?\]', x) for x in inp]
matches = [x[0][0] + x[0][1] for x in matches]
print(matches)

打印出来:

['1.40%', '1.55%', '2.17%', '1.46%', '1.46%', '1.04%', '1.41%', '1.34%',
 '1.24%', '1.27%', '14.41%', '15.54%', '14.60%', '14.27%', '4.10', '4.08',
 '4.13', '4.13', '4.07', '4.07', '4.07', '4.09', '4.03', '4.10']

上面使用的策略是在两个单独的组中匹配百分比输入中的第一个数字或方括号外的数字。然后,在列表推导中,我们将两个捕获组连接在一起。由于两个组之一保证为空,因此连接的结果始终对应于所需的匹配。

【讨论】:

    【解决方案3】:

    .group 返回捕获的组,因此.group(1) 始终返回第一个捕获的组。

    要获取另一个捕获组,请使用.group(2)

    【讨论】:

    • 但是如果我在循环中执行此操作,我如何决定要捕获哪个组。文本可以出现在任何运行?如果我可以在一行中做到这一点,我只是想避免对% 或其他方式进行条件搜索
    • @Kuni 检查表达式中括号的位置。如果捕获组不捕获任何内容,则它可以为空。
    【解决方案4】:

    另一种选择是使用环视来获得匹配:

    (?<=\()\d+(?:\.\d+)?(?=%\))|\d+(?:\.\d+)?(?=\s*\[[^][]*])
    

    模式匹配

    • (?&lt;=\() 正面向后看,向左断言 (
    • \d+(?:\.\d+)? 匹配 1+ 个数字和可选的小数部分
    • (?=%\)) 正向前瞻,向右断言 )
    • |或者
    • \d+(?:\.\d+)? 匹配 1+ 位数字和可选的小数部分
    • (?=\s*\[[^][]*]) 正向前瞻,断言从右到右的方括号(您可以通过指定方括号之间的确切格式来使其更具体)

    Regex demo | Python demo

    import re
    
    pattern = r"(?<=\()\d+(?:\.\d+)?(?=%\))|\d+(?:\.\d+)?\b(?=\s*\[[^][]*\])"
    strings = ['73 (1.40%)', '38 (1.55%)', '27 (2.17%)', '32 (1.46%)', '10 (1.46%)', '11 (1.04%)', '11 (1.41%)', '7 (1.34%)', '4 (1.24%)', '28 (1.27%)', '750 (14.41%)', '381 (15.54%)', '182 (14.60%)', '313 (14.27%)', '4.10 [3.73 - 4.45]', '4.08 [3.70 - 4.42]', '4.13 [3.77 - 4.47]', '4.13 [3.78 - 4.47]', '4.07 [3.70 - 4.42]', '4.07 [3.70 - 4.43]', '4.07 [3.70 - 4.40]', '4.09 [3.73 - 4.42]', '4.03 [3.63 - 4.40]', '4.10 [3.70 - 4.47]']
    for val in strings:
        match = re.search(pattern, val)
        print(match)
    

    输出

    <re.Match object; span=(4, 8), match='1.40'>
    <re.Match object; span=(4, 8), match='1.55'>
    <re.Match object; span=(4, 8), match='2.17'>
    <re.Match object; span=(4, 8), match='1.46'>
    <re.Match object; span=(4, 8), match='1.46'>
    <re.Match object; span=(4, 8), match='1.04'>
    <re.Match object; span=(4, 8), match='1.41'>
    <re.Match object; span=(3, 7), match='1.34'>
    <re.Match object; span=(3, 7), match='1.24'>
    <re.Match object; span=(4, 8), match='1.27'>
    <re.Match object; span=(5, 10), match='14.41'>
    <re.Match object; span=(5, 10), match='15.54'>
    <re.Match object; span=(5, 10), match='14.60'>
    <re.Match object; span=(5, 10), match='14.27'>
    <re.Match object; span=(0, 4), match='4.10'>
    <re.Match object; span=(0, 4), match='4.08'>
    <re.Match object; span=(0, 4), match='4.13'>
    <re.Match object; span=(0, 4), match='4.13'>
    <re.Match object; span=(0, 4), match='4.07'>
    <re.Match object; span=(0, 4), match='4.07'>
    <re.Match object; span=(0, 4), match='4.07'>
    <re.Match object; span=(0, 4), match='4.09'>
    <re.Match object; span=(0, 4), match='4.03'>
    <re.Match object; span=(0, 4), match='4.10'>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-24
      • 2021-06-01
      • 1970-01-01
      • 2015-07-08
      • 2010-10-03
      • 2020-03-16
      • 2010-12-24
      • 2012-01-05
      相关资源
      最近更新 更多