Python模块中寻找星号字符的正则表达式非常慢

问题描述 投票:0回答:1

我的工作环境

OS: Windows 10 (64 bits)
RAM: 32 GB
Processor: Intel(R) Xeon(R) CPU E3-1240 v5 @ 3.50 GHz
Python version: 3.7.4 (64 bits)

问题描述

我正在处理一个宁静的API日志文件。使用此API的用户可以在查询的URL中指定variableName:value,并根据变量名和指定的值,此API后面的搜索引擎将返回结果。还有一个通配符功能,允许使用正则表达式创建查询,正则表达式可以具有以下形式之一:

variableName:va*
variableName:*lue
variableName:*alu*
variableName:*

目的是读取日志文件,然后提取并计算包含上述模式之一的至少一个出现次数的行数。这可以给我们一个估计,以便我们可以在查询API时看到使用通配符功能的用户百分比。

对于我们的分析,在文件的每一行中出现多少次不同的变量(甚至可能是相同的变量)并不重要(日志文件中的每一行=一个用户查询)。一旦检测到上述模式之一的出现,就选择该行,并且我们的计数器增加,表明在查询中使用了通配符功能。

为了进行分析,我开发了一个带有以下正则表达式的Python模块:

regexp_wildcard_asterisk = r"".join(
    [
        r"[a-zA-Z][a-zA-Z0-9]*([:]|%3A)",
        r"(([*]|%2A)|[^=*]+([*]|%2A)|([*]|%2A)[^=*]+|",
        r"([*]|%2A)[^=*]+([*]|%2A))"
    ]
)
regexp_wildcard_asterisk_prog = re.compile(
    regexp_wildcard_asterisk, re.IGNORECASE
)

鉴于查询实际上是http URL,因此在上面的正则表达式中可以看到%3A和%2A,因为根据客户端:*的编码,它们也可以编码为%3A%2A分别。

然后,我要做的就是在循环内逐行读取日志文件,并检查是否存在匹配项:

with open(
        src_file,
        "r",
        encoding="UTF-8"
) as srcfile_desc:

    csv_reader = csv.reader(srcfile_desc, delimiter="|")

    wildcard_asterisk_func_counter = 0

    for tokens in csv_reader:

        # The pattern matching is done on the 5th colonne of each 
        # line, that's why I've written tokens[4]
        if (regexp_wildcard_asterisk_prog.search(tokens[4])):
            wildcard_asterisk_func_counter += 1

嗯,这可以完成工作,但是它[尽管我不得不承认有时候我的日志文件非常大,但是文件的大小仍然不能解释非常长的执行时间该程序的。上次我在只有890行且每行大约有240000个字符的日志文件上运行上述程序(只有几行有1100 000个字符)。花了超过24小时,当我检查它仍在运行时。

现在,我知道正则表达式确实可能会对性能产生影响,但是我已经对其他API日志文件进行了模式匹配,该文件包含数百万行,有时每行有数百万个字符,以查找其他字符,例如[C0 ]并且执行时间从未超过几个小时。因此,我认为正则表达式的定义中可能存在一些寻找星号的错误。

阅读我的代码,您能告诉我,您认为我在哪里犯了错(或多个错误)?

python regex
1个回答
0
投票
这对我来说比对您的教育更重要,因为您实际上如何使用上述代码有些困惑。首先,当我针对诸如?, [, ], {, }之类的输入运行您的正则表达式时,组0仅是variableName:*alu*

variableName:*

而且就搜索日志文件而言,您可以用>>> regexp_wildcard_asterisk = r"".join(
...     [
...         r"[a-zA-Z][a-zA-Z0-9]*([:]|%3A)",
...         r"(([*]|%2A)|[^=*]+([*]|%2A)|([*]|%2A)[^=*]+|",
...         r"([*]|%2A)[^=*]+([*]|%2A))"
...     ]
... )
... regexp_wildcard_asterisk_prog = re.compile(
...     regexp_wildcard_asterisk, re.IGNORECASE
... )

>>> regexp_wildcard_asterisk_prog.search('variableName:*alu*')
<_sre.SRE_Match object; span=(0, 14), match='variableName:*'>
做什么?一旦查询被验证,是否要根据是否希望variableName:*alu*匹配换行符而将所有出现的'*'和%2A替换为.*[\s\S]*?这只是完成的步骤,但您没有提及吗?实际日志文件的格式是什么?我不敢相信您提到的处理时间是在识别和验证用户查询中。一旦将其识别为使您减速的有效查询,就必须使用*进行处理。

这里是更新的程序:

variableName:*alu*

import re

regexp_wildcard_asterisk = r"""(?x) # Verbose mode
[a-zA-Z][a-zA-Z0-9]+    # Match variable
(?::|%3A)               # Match ':'
(?:
  (?:\*|%2A)?           # Match optional '*'
  (?:(?!(?:\*|%2A)).)+  # Match one or more non-'*' non-newline characters
  (?:\*|%2A)?           # Match optional '*'
  |                     # or
  (?:\*|%2A)            #  '*'
)
"""

regexp_wildcard_asterisk_prog = re.compile(
    regexp_wildcard_asterisk, re.IGNORECASE
)

lines = [
    'variableName:va*',
    'variableName:*lue',
    'variableName:*alu*',
    'variableName:*'
]

wildcard_asterisk_func_counter = 0
for line in lines:
    """
    if (regexp_wildcard_asterisk_prog.search(line)):
            wildcard_asterisk_func_counter += 1
    """
    m = regexp_wildcard_asterisk_prog.search(line)
    if m:
        wildcard_asterisk_func_counter += 1
        s = m[0]
        # convert wildcard into equivalent regex:
        s = re.sub(r'\*|%2A', '.*', s)
        s = s.replace('%3A', ':') # and these will need to be replaced, too
        #print(s)
© www.soinside.com 2019 - 2024. All rights reserved.