用于文本处理的 Regex 与 readline

Regex vs readline for text processing

我有一个文本要处理(路由器输出)并从中生成有用的数据结构(字典中的键作为 iface 名称,值作为数据包计数)。我有两种方法来完成相同的任务。我想知道我应该使用哪一种来提高效率,对于更大的数据样本,哪一种看起来更容易失败。

Readline1 从 readline 中获取一个列表并处理输出并写入字典,其中键作为接口名称,值作为接下来的三项。

Readline2 使用 re 模块并匹配组,并从组写入字典键和值。

这些函数的输入self.output将是这样的:

message = 
"""
Interface 1/1\n\t
    input : 1234\n\t
    output : 3456\n\t
    dropped : 12\n
\n
Interface 1/2\n\t
    input : 7123\n\t
    output : 2345\n\t
    dropped : 31\n\t
"""

def ReadLine1(self):
    lines = self.output.splitlines()
    for index, line in enumerate(lines):
        if "Interface" in line:
            valuelist = []
            for i in [1,2,3]:
                valuelist.append((lines[index+i].split(':'))[1].strip())
            self.IFlist[line.split()[1]] = valuelist
    return self.IFlist

def Readline2(self):
    #print repr(self.output)
    n = re.compile(r"\n*Interface (./.)\n\s*input : ([0-9]+)\n\s*output : ([0-9]+)\n\s*dropped : ([0-9]+)",re.MULTILINE|re.DOTALL)
    blocks = self.output.split('\n\n')
    for block in blocks:
        m_object = re.match(n, block)
        self.IFlist[m_object.group(1)] = [m_object.group(i) for i in (2,3,4)]

您的两种方法都使用格式的特定方面来实现您尝试进行的解析,如果该格式被更改/破坏,其中一种方法也可能会破坏...

例如,如果您在两个条目之间的空行中添加了一个 space(您看不到),那么 blocks = self.output.split('\n\n') 将无法找到两个连续的换行符,并且正则表达式版本将错过第二个条目:

{'1/1': ['1234', '3456', '13']}

或者如果您在 inputoutput 之间添加了额外的换行符,如下所示:

Interface 1/2
    input : 7123
    output : 2345

    dropped : 31

正则表达式 \s* 会处理额外的 space 但非正则表达式解析会假设 lines[index+i].split(':') 有一个索引 [1] 所以它会引发一个该数据的 IndexError

或者,如果您在任何行的末尾添加了一些额外的 space,那么正则表达式将无法在内容之后看到换行符,并且 re.match(n, lock) 将 return None 所以下一行会引发一个 AttributeError: 'NoneType' object has no attribute 'group'

或者,如果您将其中一个条目的 Interface 更改为 interface(不再是大写 I),则正则表达式会引发与上述相同的错误,但非正则表达式只会忽略该条目。

当我测试它时,我发现正则表达式更容易搞乱样本 message 的小编辑,但我也发现我使用生成器表达式和 str.partition 比他们两个强得多:

def readline3():
    gen_lines = (line for line in self.output.splitlines()
                        if line and not line.isspace())
    try:
        while True: #ended when next() throws a StopIteration
            start,_,key = next(gen_lines).partition(" ")
            if start == "Interface":
                IFlist[key] = [next(gen_lines).rpartition(" : ")[2]
                                for _ in "123"]
    except StopIteration: # reached end of output
        return self.IFlist

这在上面提到的每一个案例和其他几个案例中都成功了,因为它依赖的唯一方法是 str.partition,它总是 returns 一个 3 项元组,没有什么可以引发任何意想不到的错误,除非 self.output 不是字符串。

另外 运行 使用 timeit 的基准测试,您的 readline1 始终比 readline2 快,而我的 readline3 通常比 readline1:

#using the default 1000000 loops using 'message'
<function readline1 at 0x100756f28>
11.225649802014232
<function readline2 at 0x1057e3950>
14.838601427007234
<function readline3 at 0x1057e39d8>
11.693351223017089