使用正则表达式从文件中提取值的简单方法 (python)

Simple way of extracting a value from a file using regex (python)

你好,我目前正在 python 学习正则表达式,为自己做了一个简单的练习,我有一个充满数据行的文件,我只想从具有的每一行中提取一个值里面有‘外’。

file.txt

ABC 134234ed6  outer +
deE  325353ed5 out +
ABC 133234ed0 outer +
deE  325353ed5 out +
ABC 135234ed0 outer +
deE 125353ed5  out +
ABC 455234ed0  outer +
deE 125353ed5  out +

在这里,我只需要在其中包含outer的每一行中获取ed(6,0,0,0)之后的数字。 我的代码目前可以正常工作,但我想知道是否有简化的方法来做到这一点,只使用正则表达式。

这是我的代码:

main.py

import re

with open('file.txt') as f:
    lines = f.readlines()

regex = re.compile(r'\d +(outer) \+$')
results = []

for line in lines:
    match = regex.search(line)
    if match:
        result = match.group()
        results.append(int(result.split(' ')[0])) # this

print(results)

它打印出我想要的 [6, 0, 0, 0]。但是逻辑涉及拆分字符串然后获取第一项(标记为 # this 的行),我相信可以直接将其放入正则表达式中,并且可以直接使用 group() 提取值。

我知道已经有类似的问题了,但我觉得我的问题很具体,你只需要帮助我简化逻辑,谢谢!

您可以重构您的代码并删除所有冗余的正则表达式拆分,匹配:

import re

with open('file.txt') as f:
    lines = f.readlines()

reg = re.compile(r'(\d+) +outer \+$')
results = []

for line in lines:
   m = reg.search(line)
   if m:
       results.append( int(m.group(1)) )

print (results)

输出:

[6, 0, 0, 0]

Code Demo

正则表达式详细信息:

  • (\d+) +: Match 1+ digits and capture this in group #1 followed by 1+ spaces. Note that you are only interested in getting this value (\d+)` 因此在捕获组中使用它。
  • outer \+:匹配 outer 后跟 space 和 + 字符
  • $: 比赛结束

情况一:"outer",如果存在,必须跟在"edX"

之后

在这种情况下,您可以使用正则表达式匹配字符串

r'(?<=ed)\d+(?=.*\bouter\b)'

如果有匹配项,它将是 "ed".

之后的数字

Demo | Python code

Python 的正则表达式引擎执行以下操作。

(?<=ed)         : positive lookbehind asserts that current position
                  is immediately preceded by 'ed'
\d+             : match 1+ digits
(?=.*\bouter\b) : positive lookahead asserts that current match is
                  followed by 0+ characters other than line terminators,
                  followed by 'outer' with word boundaries

情况 2:"outer",如果存在,可能在 "edX"

之前或之后

在这种情况下,您可以将字符串与正则表达式匹配

r'^(?=.*\bouter\b).*ed(\d+)'

如果匹配,"ed" 之后的数字将包含在捕获组 1 中。

Demo | Python code

Python 的正则表达式引擎执行以下操作。

^               : assert beginning of string
(?=.*\bouter\b) : positive lookahead asserts that the string
                  contains 'outer' with word boundaries
.*ed            : match 0+ characters other than line terminators,
                  followed by 'ed'
(\d+)           : match 1+ digits in capture group 1

存在单词边界 (\b) 以避免匹配单词,例如 "router""accouterment"

基本要点是您应该对正则表达式中您有兴趣提取的部分使用分组括号。最小的修复是将 () 放在 \d 周围,而不是 outer,这样您就可以使用 match.group(1) —— 请参阅 anubhava 的回答。除此之外,因为您实际上已经将整个文件读入内存,所以显然没有必要通过一次读取一行来减少内存,实际上您可以将其作为字符串读入,然后使用 re.finditer.这将有助于简化代码。示例:

import re

with open('file.txt') as f:
    text = f.read()

regex = re.compile(r'(\d) +outer \+\n')

results = [int(match.group(1)) for match in regex.finditer(text)]

print(results)

这给出:

[6, 0, 0, 0]

请注意,在正则表达式中现在有 \n(换行符)来替换原始正则表达式中的 $——outer \+ 必须后跟换行符。


附录

回答如果文件真的很大怎么办的问题:正如如果它会超出可用内存则不能使用 f.readlines() 一样,您也不能使用 f.read()。您最好的方法可能是以下方法(类似于 anubhava 的回答,但避免使用 readlines)。请注意,在正则表达式中使用捕获组的基本问题仍然相同。

import re

results = []
matcher = re.compile(r'(\d) +outer \+$').search
with open('file.txt') as f:
    for line in f:
        match = matcher(line)
        if match:
            results.append(int(match.group(1)))

print(results)
import re

with open('file.txt') as f:
    lines = f.readlines()

results_str = re.findall(r'.*ed(\d+).*\bouter\b.*\+', ''.join(lines))

results = [int(x) for x in results_str]

print(results)

输出:

[6, 0, 0, 0]

re.findall 可用于查找字符串中的所有匹配项

Return a list of all non-overlapping matches in the string. If one or more capturing groups are present in the pattern, return a list of groups; this will be a list of tuples if the pattern has more than one group.