用python编写一个快速解析器
Posted
技术标签:
【中文标题】用python编写一个快速解析器【英文标题】:writing a fast parser in python 【发布时间】:2011-02-12 23:01:22 【问题描述】:我已经为我们在一次讲座中使用的某种文件格式 (ARFF) 编写了一个动手操作的递归纯 Python 解析器。现在运行我的练习提交非常慢。到目前为止,大部分时间都花在了我的解析器上。消耗大量CPU时间,HD不是瓶颈。
我想知道在 python 中编写解析器有哪些高效的方法?我宁愿不要用 C 重写它。我尝试使用 jython,但这大大降低了性能!我解析的文件部分很大(> 150 MB),行很长。
我当前的解析器只需要一个字符的前瞻。我会在这里发布源代码,但我不知道这是否是个好主意。毕竟提交截止日期还没有结束。但是,本练习的重点不是解析器。您可以选择您想使用的任何语言,并且已经有 Java 解析器。
注意:我有一个 x86_64 系统,所以 psyco(它似乎也是 PyPy)不是选项。
更新:我现在将我的解析器/写入器上传到 bitbucket。
【问题讨论】:
你分析过你的解析器吗?可能只是阻碍一切的瓶颈之一。 没有代码示例就不可能给出体面的建议。您可能正在使用一种存在重大缺陷的合理技术,或者您的整个方法可能需要重新设计,我们无法知道。 你试过用 psyco 搭配它吗? 我使用scipy.io.arff.loadarff
解析 arff 文件 - 不确定它是否符合规模,但它对我很有帮助。
【参考方案1】:
您可以使用ANTLR 或pyparsing,它们可能会加快您的解析过程。
如果您想保留当前代码,您可能需要查看Cython/PyPy,这可以提高您的性能(有时高达 4 倍)。
【讨论】:
pyparsing 不太可能加快速度,但可能会对瓶颈所在位置有所了解。另外,我相信已经编写了一个 ARFF pyparsing 解析器,并且已经在某个地方。 确实如此——而且我不知道 pyparsing 与 PyPy 或 Cython 的匹配度如何。 从 weka 网站链接的 ARFF pyparsing 解析器非常不完整(如果你说的是那个)。我也尝试过 cython,但因为我经常使用 yield,所以我不得不使用 hg 版本,而我所做的只是产生段错误的代码。 PyPy 似乎只有 32 位。至少它没有为我编译。我有一个 x86_64 系统。还有 32 个二进制文件,它们提供了针对我尚未安装的库的链接。 pyparsing 与当前版本的 PyPy 完全兼容,并且在 PyPy 上的运行速度大约是在 CPython 上的两倍。【参考方案2】:在没有更多信息的情况下,我给出的最一般的提示是一次将整个文件或至少其中的大部分文件读入内存。您不想一次读一个字符并到处寻找;不管幕后发生的缓冲如何,将整个东西都放在内存中可能是个好主意,这样您就可以随心所欲地对其进行操作。
我已经用 Python 编写了解析器,并且没有特别要求它们比用任何其他语言编写的解析器要特别慢。与这类事情一样,您更有可能在做您不需要做的工作。在这些物品类别中,创建、销毁和重新创建同一个对象比将其存储在某个地方更昂贵。一遍又一遍地重新计算一个值比将它存储在某个地方更昂贵。等等等等。
特别是在 Python 中,人们陷入的一个陷阱是进行大量不必要的字符串操作。不要一次附加一个字符的字符串;当你建立你的令牌时,在“主”字符串上做你的工作,并一举去除令牌。 (换句话说,索引到“master”字符串,找出起点和终点,然后用token = master[start:end]
抓取它。)一次一个字符的字符串连接是导致性能下降的捷径。我怀疑即使您出于某种原因想要/需要做for c in master: newstr += c
,您可能会更幸运地将'c'塞入列表然后newstr = ''.join(newstr_charlist)
。
【讨论】:
我经常使用这样的东西,这是最快的方法吗?但是,我的用例不会触发此特定代码。 pastebin.com/yAmEcKB8 哦,我从文件中读取了 4096 字节的块(readc() 和 peek() 方法对这些块进行操作)。我不认为读取洞文件是一个好主意,因为这些文件的大小会超过 150 MB。 现代计算机有 512M 或更多的内存。读取 150MB 不算什么。 :) 代码是生成器的一个有趣用途,但我担心函数调用开销。也许它非常好,但我可能会尝试在没有生成器的情况下进行实现,只需索引字符串,看看是否有任何特定的性能差异。 CPython 中的函数调用非常昂贵,编写一个好的、通用的解析器而不进行函数调用是不优雅的。以上是关于用python编写一个快速解析器的主要内容,如果未能解决你的问题,请参考以下文章