如何更快地遍历这个文本文件?
我有很多部分在这个格式的文件:如何更快地遍历这个文本文件?
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循环。我想知道我能做些什么来加速它。
当你需要花哨的模式匹配时,正则表达式非常棒,但是当你不需要时,使用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
。
'而不是“};”在nextLine中:' - >这将从头到尾扫描每一行(至少我认为是这样,我不知道Python)。所以它可能(稍微)更快,只检查行的前两个字节。 –
@Danny_ds我决定离开这一行,因为它是在OP的代码,因为我想专注于正则表达式的东西,因为这是可以改进的主要事情。是的,'“};” nextLine'确实对线条进行了线性扫描,但是该扫描以C速度运行,所以速度比使用Python循环搜索时快。当然,我可以检查该行的前两个字符(它们不是Python 3中的字节,因为它使用Unicode作为文本),例如通过使用'.startswith'方法,但是我不得不假设那里不是前导空格,或者先通过'.strip'来修剪空格。 –
是的,C速度,可能已经在L1缓存中 - 这就是为什么我使用_slightly_ :)并且使用'.strip'可能会使它变得更糟,这取决于实现。无论如何。 –
您可以使用带有正则表达式的生成器 - 如果您提供了一些真实的样本,则可以更好地帮助您。 – Jan
它慢多少? –
@Jan我无法提供原始文件,但我会看看我是否可以自己创建样本。 – Colin