2017-11-25 204 views
6

我有很多部分在这个格式的文件:如何更快地遍历这个文本文件?

section_name_1 <attribute_1:value> <attribute_2:value> ... <attribute_n:value> { 
    field_1 finish_num:start_num some_text ; 
    field_2 finish_num:start_num some_text ; 
    ... 
    field_n finish_num:start_num some_text; 
}; 

section_name_2 ... 
... and so on 

该文件可以是几十万排长队的。每个部分的属性和字段数可以不同。我想建立一些字典来保存这些值。我已经有一个单独的字典,它包含所有可能的'属性'值。

import os, re 
from collections import defaultdict 

def mapFile(myFile, attributeMap_d): 
     valueMap_d = {} 
     fieldMap_d = defaultdict(dict) 

     for attributeName in attributeMap_d: 
      valueMap_d[attributeName] = {} 

     count = 0 
     with open(myFile, "rb") as fh: 
      for line in fh: 
       # only look for lines with < 
       if '<' in line: 
        # match all attribute:value pairs inside <> brackets 
        attributeAllMatch = re.findall(r'<(\S+):(\S+)>', line) 
        attributeAllMatchLen = len(attributeAllMatch) 
        count = 0 

        sectionNameMatch = re.match(r'(\S+)\s+<', line) 

        # store each section name and its associated attribute and value into dict 
        for attributeName in attributeMap_d: 
         for element in attributeAllMatch: 
          if element[0] == attributeName: 
           valueMap_d[attributeName][sectionNameMatch.group(1).rstrip()] = element[1].rstrip() 
           count += 1 
         # stop searching if all attributes in section already matched 
         if count == attributeAllMatchLen: break 

        nextLine = next(fh) 

        #in between each squiggly bracket, store all the field names and start/stop_nums into dict 
        #this while loop is very slow... 
        while not "};" in nextLine: 
         fieldMatch = re.search(r'(\S+)\s+(\d+):(\d+)', nextLine) 
         if fieldMatch: 
          fieldMap_d[sectionNameMatch.group(1)][fieldMatch.group(1)] = [fieldMatch.group(2), fieldMatch.group(3)] 
         nextLine = next(fh) 

     return valueMap_d 

我的问题是,while循环,所有的字段值相匹配是明显比其余代码的要慢0.5秒与2.2S根据CPROFILE如果我删除了while循环。我想知道我能做些什么来加速它。

+1

您可以使用带有正则表达式的生成器 - 如果您提供了一些真实的样本,则可以更好地帮助您。 – Jan

+0

它慢多少? –

+0

@Jan我无法提供原始文件,但我会看看我是否可以自己创建样本。 – Colin

回答

2

当你需要花哨的模式匹配时,正则表达式非常棒,但是当你不需要时,使用str方法解析文本会更快。以下是一些代码,比较使用正则表达式进行字段解析的时间,并与str.split进行比较。

首先,我创建了一些假存储在rows列表中的假测试数据。这样做使我的演示代码比从文件读取数据更简单,但更重要的是,它消除了文件读取的开销,因此我们可以更准确地比较解析速度。

顺便说一句,你应该在字段解析循环之外保存sectionNameMatch.group(1),而不是必须在每个字段行上进行该调用。

首先,我将说明我的代码正确解析数据。 :)

import re 
from pprint import pprint 
from time import perf_counter 

# Make some test data 
num = 10 
rows = [] 
for i in range(1, num): 
    j = 100 * i 
    rows.append(' field_{:03} {}:{} some_text here ;'.format(i, j, j - 50)) 
rows.append('};') 
print('\n'.join(rows)) 

# Select whether to use regex to do the parsing or `str.split` 
use_regex = True 
print('Testing {}'.format(('str.split', 'regex')[use_regex])) 

fh = iter(rows) 
fieldMap = {} 

nextLine = next(fh) 
start = perf_counter() 
if use_regex: 
    while not "};" in nextLine: 
     fieldMatch = re.search(r'(\S+)\s+(\d+):(\d+)', nextLine) 
     if fieldMatch: 
      fieldMap[fieldMatch.group(1)] = [fieldMatch.group(2), fieldMatch.group(3)] 
     nextLine = next(fh) 
else: 
    while not "};" in nextLine: 
     if nextLine: 
      data = nextLine.split(maxsplit=2) 
      fieldMap[data[0]] = data[1].split(':') 
     nextLine = next(fh) 

print('time: {:.6f}'.format(perf_counter() - start)) 
pprint(fieldMap) 

输出

field_001 100:50 some_text here ; 
field_002 200:150 some_text here ; 
field_003 300:250 some_text here ; 
field_004 400:350 some_text here ; 
field_005 500:450 some_text here ; 
field_006 600:550 some_text here ; 
field_007 700:650 some_text here ; 
field_008 800:750 some_text here ; 
field_009 900:850 some_text here ; 
}; 
Testing regex 
time: 0.001946 
{'field_001': ['100', '50'], 
'field_002': ['200', '150'], 
'field_003': ['300', '250'], 
'field_004': ['400', '350'], 
'field_005': ['500', '450'], 
'field_006': ['600', '550'], 
'field_007': ['700', '650'], 
'field_008': ['800', '750'], 
'field_009': ['900', '850']} 

下面是与use_regex = False输出;我不打扰重新打印输入数据。

Testing str.split 
time: 0.000100 
{'field_001': ['100', '50'], 
'field_002': ['200', '150'], 
'field_003': ['300', '250'], 
'field_004': ['400', '350'], 
'field_005': ['500', '450'], 
'field_006': ['600', '550'], 
'field_007': ['700', '650'], 
'field_008': ['800', '750'], 
'field_009': ['900', '850']} 

现在进行真正的测试。我将设置num = 200000并注释掉打印输入数据的行。

Testing regex 
time: 3.640832 

Testing str.split 
time: 2.480094 

正如你所看到的,正则表达式的版本慢了大约50%。

这些时间是在我运行Python 3.6.0的古老的2GHz 32位机器上获得的,所以你的速度可能不同。 ;)如果你的Python没有time.perf_counter,你可以改用time.time

+0

'而不是“};”在nextLine中:' - >这将从头到尾扫描每一行(至少我认为是这样,我不知道Python)。所以它可能(稍微)更快,只检查行的前两个字节。 –

+0

@Danny_ds我决定离开这一行,因为它是在OP的代码,因为我想专注于正则表达式的东西,因为这是可以改进的主要事情。是的,'“};” nextLine'确实对线条进行了线性扫描,但是该扫描以C速度运行,所以速度比使用Python循环搜索时快。当然,我可以检查该行的前两个字符(它们不是Python 3中的字节,因为它使用Unicode作为文本),例如通过使用'.startswith'方法,但是我不得不假设那里不是前导空格,或者先通过'.strip'来修剪空格。 –

+0

是的,C速度,可能已经在L1缓存中 - 这就是为什么我使用_slightly_ :)并且使用'.strip'可能会使它变得更糟,这取决于实现。无论如何。 –