【问题标题】:Efficient regex with lists带有列表的高效正则表达式
【发布时间】:2018-06-04 06:46:52
【问题描述】:

我有一个来自os.listdir() 的字符串列表,如下所示:

['foo',
 'bar'
 'backup_20180406'
 ...]

在这些条目中,我想获得与 "backup_YYYYMMDD" 模式匹配的条目。带有命名组的正则表达式将是

regex = r"BACKUP_(?P<date>\d+)"

我正在尝试创建一个包含上述 date only 的列表(又名.group('date')),但如果不解析字符串两次,我找不到方法。

res = [re.search(regex, x).group('date') for x in filter(r.match, os.listdir(folder))]

我确信我在这里遗漏了一些非常明显和简洁的东西,那么有没有更好的方法?

【问题讨论】:

  • 也许re.findall(r"(?i)BACKUP_(\d+)", " ".join(os.listdir(folder))) 可以吗?
  • @WiktorStribiżew 也想过,但我不喜欢 join 位。再说一次,使用奇怪的字符作为分隔符并调整正则表达式应该使它非常稳定
  • 你需要一些临时变量;没有办法解决它。列表推导不适合这种事情。
  • @Ev.Kounis,记得选择答案

标签: python regex performance


【解决方案1】:

我通常这样做:

regex = re.compile(r"BACKUP_(?P<date>\d+)")
a = ['foo', "BACKUP_20180406", 'xxx']
matches = [regex.match(x) for x in a]
valid = [x.group('date') for x in matches if x]

或者只是

valid = [x.group('date') for x in (regex.match(y) for y in a) if x]

另请注意,regex.matchregex.search(在适用时)快得多 - 即当您从行首搜索时。

【讨论】:

  • @Ev.Kounis 怎么样?您的正则表达式匹配两次;这个没有。
  • @Aran-Fey 你是对的,我想这是其中一个令人困惑的星期一。我现在真的很喜欢这个答案/
【解决方案2】:

这是一个 Pandas 解决方案,使用 extract()

import pandas as pd

strings = ['foo', 'bar', 'backup_20180406']
regex = r"backup_(?P<date>\d+)"

pd.Series(strings).str.extract(regex).dropna()

2    20180406
Name: date, dtype: object

【讨论】:

  • 有趣.. +1,虽然我不想导入其他任何东西。
  • 看来这个解决方案不是很快(见我的回答)
【解决方案3】:

如果您正在测试一个简单的模式,正则表达式很少是最有效的工具。 只需使用startswith,速度就会提高一倍。

from timeit import timeit
import re

size = 10000
data = ['foo','bar','backup_20180406'] * size

def find_dates(data):
    prefix = 'backup_'
    prefix_size = len(prefix)
    return [name[prefix_size:] for name in data if name.startswith(prefix)]

def find_dates_testing_numbers(data):
    prefix = 'backup_'
    prefix_size = len(prefix)
    for name in data:
        if name.startswith(prefix):
            try:
                yield int(name[prefix_size:])
            except ValueError:
                pass

def find_dates_regex(data):
    regex = re.compile(r"backup_(?P<date>\d+)")
    return [x.group('date') for x in (regex.match(y) for y in data) if x]

def find_dates_pd(data):
    import pandas as pd
    regex = r"backup_(?P<date>\d+)"
    return pd.Series(data).str.extract(regex).dropna()

result = find_dates(data)
print(timeit('find_dates(data)', globals=globals(), number=1000))
# 4.02514289499959 seconds

print(timeit('list(find_dates_testing_numbers(data))', globals=globals(), number=1000))
# 6.0529899510002 seconds

print(timeit('find_dates_regex(data)', globals=globals(), number=1000))
# 8.772153561999403 seconds

print(timeit('find_dates_pd(data)', globals=globals(), number=1000))
# 19.018224569999802

【讨论】:

  • 字符串操作的解决方案可能是最快的,但它不会检查文件名是否以日期结尾。它还会接收backup_foo 之类的内容。
  • 你是对的。已编辑。仍然比正则表达式快。当然,正则表达式非常有用且功能强大,但是当测试的模式非常简单时,它们会变慢。
  • 100% 同意您关于尽可能避免使用正则表达式的评论。但这不是原始问题的答案
  • 我并不是说你必须避免使用正则表达式。我说你必须根据自己的需要选择最好的工具。
猜你喜欢
  • 2022-12-05
  • 1970-01-01
  • 1970-01-01
  • 2010-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-31
  • 2020-02-24
相关资源
最近更新 更多