gpt4 book ai didi

python - 提高分隔配置 block 的解析速度

转载 作者:太空宇宙 更新时间:2023-11-03 16:19:15 26 4
gpt4 key购买 nike

我有一个相当大的配置文件,由以 分隔的 block 组成

#start <some-name> ... #end <some-name>some-name该 block 必须相同。该 block 可以出现多次,但永远不会包含在其自身内。只有一些其他 block 可能出现在某些 block 中。我对这些包含的 block 不感兴趣,而是对第二层中的 block 感兴趣。

在真实文件中,名称不以blockX开头。但彼此之间有很大不同。

一个例子:

#start block1

#start block2

/* string but no more name2 or name1 in here */
#end block2

#start block3
/* configuration data */
#end block3

#end block1

这是使用正则表达式进行解析的,并且在没有附加调试器的情况下运行时速度相当快。 2k 2.7MB 文件需要 0.23 秒,简单规则如下:

blocks2 = re.findAll('#start block2\s+(.*?)#end block2', contents)

我尝试用 pyparsing 解析它,但即使没有附加调试器,速度也非常慢,同一文件需要 16 秒。

我的方法是生成一个 pyparsing 代码,该代码将模仿正则表达式的简单解析,因此我现在可以使用一些其他代码,并避免现在必须解析每个 block 。语法相当广泛。

这是我尝试过的

block = [Group(Keyword(x) + SkipTo(Keyword('#end') + Keyword(x)) + Keyword('#end') - x )(x + '*') for x in ['block3', 'block4', 'block5', 'block6', 'block7', 'block8']]

blocks = Keyword('#start') + block

x = OneOrMore(blocks).searchString(contents) # I also tried parseString() but the results were similar.

我做错了什么?我如何优化它以接近正则表达式实现所达到的速度?

编辑:与真实数据相比,上一个示例非常简单,因此我现在创建了一个合适的示例:

/* all comments are C comments */
VERSION 1 0
#start PROJECT project_name "what is it about"
/* why not another comment here too! */
#start SECTION where_the_wild_things_are "explain this section"


/* I need all sections at this level */

/* In the real data there are about 10k of such blocks.
There are around 10 different names (types) of blocks */


#start INTERFACE_SPEC
There can be anything in the section. Not Really but i want to skip anything until the matching (hash)end.
/* can also have comments */

#end INTERFACE_SPEC

#start some_other_section
name 'section name'

#start with_inner_section
number_of_points 3 /* can have comments anywhere */
#end with_inner_section
#end some_other_section /* basically comments can be anywhere */

#start some_other_section
name 'section name'
other_section_attribute X
ref_to_section another_section
#end some_other_section

#start another_section
degrees
#start section_i_do_not_care_about_at_the_moment
ref_to some_other_section
/* of course can have comments */
#end section_i_do_not_care_about_at_the_moment
#end another_section

#end SECTION
#end PROJECT

为此,我必须扩展您最初的建议。我对两个外部 block (项目和部分)进行了硬编码,因为它们必须存在。

在此版本中,时间仍为 ~16 秒:

def test_parse(f):
import pyparsing as pp
import io
comment = pp.cStyleComment

start = pp.Literal("#start")
end = pp.Literal("#end")
ident = pp.Word(pp.alphas + "_", pp.printables)

inner_ident = ident.copy()
inner_start = start + inner_ident
inner_end = end + pp.matchPreviousLiteral(inner_ident)
inner_block = pp.Group(inner_start + pp.SkipTo(inner_end) + inner_end)

version = pp.Literal('VERSION') - pp.Word(pp.nums)('major_version') - pp.Word(pp.nums)('minor_version')

project = pp.Keyword('#start') - pp.Keyword('PROJECT') - pp.Word(pp.alphas + "_", pp.printables)(
'project_name') - pp.dblQuotedString + pp.ZeroOrMore(comment) - \
pp.Keyword('#start') - pp.Keyword('SECTION') - pp.Word(pp.alphas, pp.printables)(
'section_name') - pp.dblQuotedString + pp.ZeroOrMore(comment) - \
pp.OneOrMore(inner_block) + \
pp.Keyword('#end') - pp.Keyword('SECTION') + \
pp.ZeroOrMore(comment) - pp.Keyword('#end') - pp.Keyword('PROJECT')

grammar = pp.ZeroOrMore(comment) - version.ignore(comment) - project.ignore(comment)

with io.open(f) as ff:
return grammar.parseString(ff.read())

编辑:错字,说是 2k,但实际上是 2.7MB 文件。

最佳答案

首先,发布的这段代码对我不起作用:

blocks = Keyword('#start') + block

更改为:

blocks = Keyword('#start') + MatchFirst(block)

至少与您的示例文本相符。

您可以尝试使用 pyparsing 的自适应表达式之一,matchPreviousLiteral,而不是硬编码所有关键字:

(已编辑)

def grammar():
import pyparsing as pp
comment = pp.cStyleComment

start = pp.Keyword("#start")
end = pp.Keyword('#end')
ident = pp.Word(pp.alphas + "_", pp.printables)
integer = pp.Word(pp.nums)

inner_ident = ident.copy()
inner_start = start + inner_ident
inner_end = end + pp.matchPreviousLiteral(inner_ident)
inner_block = pp.Group(inner_start + pp.SkipTo(inner_end) + inner_end)

VERSION, PROJECT, SECTION = map(pp.Keyword, "VERSION PROJECT SECTION".split())

version = VERSION - pp.Group(integer('major_version') + integer('minor_version'))

project = (start - PROJECT + ident('project_name') + pp.dblQuotedString
+ start + SECTION + ident('section_name') + pp.dblQuotedString
+ pp.OneOrMore(inner_block)('blocks')
+ end + SECTION
+ end + PROJECT)

grammar = version + project
grammar.ignore(comment)

return grammar

只需在语法中最顶层的表达式上调用 ignore() - 它将向下传播到所有内部表达式。另外,如果您已经调用了 ignore(),则无需在语法中添加 ZeroOrMore(comment)

我在大约 16 秒内解析了 2MB 输入字符串(包含 10,000 个内部 block ),因此 2K 文件只需要大约 1/1000 的时间。

关于python - 提高分隔配置 block 的解析速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38662521/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com