【问题标题】:Python: efficiently checking a file line by linePython:有效地逐行检查文件
【发布时间】:2015-07-15 20:43:40
【问题描述】:

我有非常大的文本文件(大约 150 万行或更多行)来解析一些信息。我所做的每一行都会检查某些关键字(我称它们为“标志”)。

但是,我为每一行运行了数十次检查,因此我的程序需要相当长的时间才能完成。有没有更快的方法来检查我目前的检查方式?下面只是我正在做的一个例子:

nameFound = false
ageFound = false

for line in file:
    if not nameFound and line.find('name:') != -1:
        do something
    elif not ageFound and line.find('age:') != -1:
        do something
    elif line.find('test pass') != -1:
        do something
    elif line.find('test fail') != -1:
        do something
    and so on ...

一些“标志”只出现一次(尽管我不知道文件中的位置或顺序),所以我使用“找到”变量来缩短对这些“标志”的检查。其他信息出现了数千次,所以我不能在我的条件下使用“找到”变量。 我知道的一件事是,如果每一行包含一个“标志”,则最多只有一个标志。考虑到这些信息,有没有更有效的方法来解决这个问题?

【问题讨论】:

  • 我会首先在您的代码上运行 Python 分析器以找出性能瓶颈所在。 https://docs.python.org/2/library/profile.html。用结果修改您的问题,我们可以从那里开始工作。
  • 我认为您使用elif 语句是对标志执行短路测试的最有效方式。
  • 您做了很多额外的工作,因为对于您失败的每个标志,您都会再次解析 整个 行。
  • in 明显快于str.find
  • 我将代码改为in,但没有看到明显的改进

标签: python performance file if-statement for-loop


【解决方案1】:

只是一个建议:您可以使用“标志”列表,并遍历它们。

flags = [('name:', nameMethod), ('age:', ageMethod), ('test pass', tpMethod), ('test fail', tfMethod), ... ] #methods are functions without ()

for line in lines:
  for flag, func in flags:
    if line.find(flag) != -1:
      func(args) #your args
      break #same functionality as elif

顺便说一句,使用in 运算符比str.findstr.index 更好。所以if flag in line: #do something

这只是为了便于阅读。您绝对应该进行分析以识别瓶颈,然后看看应该/不应该修复什么。我确实知道使用in 而不是str.find 效率更高(根据我的测试估计大约是3 倍)。

【讨论】:

  • 谢谢,使用 in 运算符帮助很大。还有没有办法用短路方法来实现你的标志列表?还是我根本不应该打扰它?
  • @mistasong 列表只会提高可读性。我认为 for 循环和 if 语句之间不会有相当大的时间差异,但您可以使用 time 模块进行计时。
【解决方案2】:

在 Python 中,您无法加快速度。您将逐行进行,只读取一次文件。你已经做了很多工作来通过短路“挤压”更多的速度。您的代码运行所需的时间与文件的大小成正比。

您最好先使用更快的工具过滤文件的行。例如,grep Unix 命令通常用 C 实现,而且速度非常快(比用 Python 实现循环快得多)。因此,您可以使用这样的命令(在 Linux 或 Unix 上)过滤行,然后在过滤后的文件上运行您的程序:

grep "flag1\|flag2\|flag3" big.txt > filtered.txt

另一种选择可能是使用 Cython 尝试通过将代码编译为 C 来加速代码。但实际上,这不会为您带来太多

【讨论】:

  • 检测到cat的无用使用!
  • 是的,我总是忘记 grep 的参数顺序,所以我只是 cat 或通过管道将文本输入...我在手册页中查找并修复了它:)
【解决方案3】:

考虑一下:每个if 都会在整个 行中搜索一个(可能不存在的)标志。

假设您的线路是“爸爸已到达机场”,而您的标志列表是 ['M', 'Mommy'],那么很明显,如果找不到 M,那么也找不到妈妈。

如果您愿意努力实现它们,Aho-CorasickRabin-Karp 算法将提供显着的加速。

【讨论】:

    【解决方案4】:

    正则表达式搜索可能更快:

    import re
    flags = "name|age|etc"
    
    for line in file:
        m = re.match(flags, line)
        if m.group():
            do_something(m)
    

    或者,您可以将所有测试放在一个 while 循环中,以便它在找到标志后立即退出:

    flags = ['name', 'age', 'foo']
    
    for line in file:
        i = 0
        while i < len(flags):
            if (line.find(flags[i])):
                do_something(flags[i])
                i = len(flag[i]) 
            i += 1
    

    如果你需要为每个标志做不同的事情,你可以把那个逻辑放在一个方法中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-15
      • 2017-04-28
      • 2015-07-03
      • 1970-01-01
      • 2012-04-13
      • 1970-01-01
      相关资源
      最近更新 更多