最快的方式来" grep"大文件

时间:2016-11-29 14:39:22

标签: python python-2.7

我有大的日志文件(从100MB到2GB),包含我需要在Python程序中解析的(单个)特定行。我必须解析大约20,000个文件。我知道搜索到的行在文件的最后200行内,或者在最后15000字节内。

由于这是一项经常性的任务,我需要尽可能快。获得它的最快方法是什么?

我考虑过4种策略:

  • 用Python读取整个文件并搜索正则表达式(method_1)
  • 只读取文件的最后15,000个字节并搜索正则表达式(method_2)
  • 对grep(method_3)进行系统调用
  • 在拖尾最后200行(method_4)
  • 后对grep进行系统调用

以下是我为测试这些策略而创建的函数:

import os
import re
import subprocess

def method_1(filename):
    """Method 1: read whole file and regex"""
    regex = r'\(TEMPS CP :[ ]*.*S\)'
    with open(filename, 'r') as f:
        txt = f.read()
    match = re.search(regex, txt)
    if match:
        print match.group()

def method_2(filename):
    """Method 2: read part of the file and regex"""
    regex = r'\(TEMPS CP :[ ]*.*S\)'
    with open(filename, 'r') as f:
        size = min(15000, os.stat(filename).st_size)
        f.seek(-size, os.SEEK_END)
        txt = f.read(size)
        match = re.search(regex, txt)
        if match:
            print match.group()

def method_3(filename):
    """Method 3: grep the entire file"""
    cmd = 'grep "(TEMPS CP :" {} | head -n 1'.format(filename)
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
    print process.communicate()[0][:-1]

def method_4(filename):
    """Method 4: tail of the file and grep"""
    cmd = 'tail -n 200 {} | grep "(TEMPS CP :"'.format(filename)
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
    print process.communicate()[0][:-1]

我在两个文件上运行这些方法(" trace"是207MB," trace_big"是1.9GB)并获得以下计算时间(以秒为单位):

+----------+-----------+-----------+
|          |   trace   | trace_big |
+----------+-----------+-----------+
| method_1 | 2.89E-001 | 2.63      |
| method_2 | 5.71E-004 | 5.01E-004 |
| method_3 | 2.30E-001 | 1.97      |
| method_4 | 4.94E-003 | 5.06E-003 |
+----------+-----------+-----------+

所以method_2似乎是最快的。但还有其他我没想过的解决方案吗?

修改

除了以前的方法,Gosha F建议使用mmap的第五种方法:

import contextlib
import math
import mmap

def method_5(filename):
    """Method 5: use memory mapping and regex"""
    regex = re.compile(r'\(TEMPS CP :[ ]*.*S\)')
    offset = max(0, os.stat(filename).st_size - 15000)
    ag = mmap.ALLOCATIONGRANULARITY
    offset = ag * (int(math.ceil(offset/ag)))
    with open(filename, 'r') as f:
        mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_COPY, offset=offset)
        with contextlib.closing(mm) as txt:
            match = regex.search(txt)
            if match:
                print match.group()

我测试了它并获得了以下结果:

+----------+-----------+-----------+
|          |   trace   | trace_big |
+----------+-----------+-----------+
| method_5 | 2.50E-004 | 2.71E-004 |
+----------+-----------+-----------+

3 个答案:

答案 0 :(得分:6)

您也可以考虑使用内存映射(mmap模块),如下所示

def method_5(filename):
    """Method 5: use memory mapping and regex"""
    regex = re.compile(r'\(TEMPS CP :[ ]*.*S\)')
    offset = max(0, os.stat(filename).st_size - 15000)
    with open(filename, 'r') as f:
        with contextlib.closing(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_COPY, offset=offset)) as txt:
            match = regex.search(txt)
            if match:
                print match.group()

也有一些旁注:

  • 在使用shell命令的情况下,ag在某些情况下可能比grep快几个数量级(尽管只有200行可greppable文本,与启动shell的开销相比,差异可能会消失)
  • 只是在函数开头编译你的正则表达式可能会有所不同

答案 1 :(得分:2)

在shell中进行处理可能会更快,以避免python开销。然后你可以将结果传递给python脚本。否则看起来你做的最快。

寻求正则表达式匹配应该非常快。方法2和方法4是相同的,但你需要额外的python开发系统调用。

答案 2 :(得分:2)

是否必须使用Python?为什么不是shell脚本?
我的猜测是方法4将是最快/最有效的。这肯定是我将它写成shell脚本的方式。并且它比1或3更快。但是,与方法2相比,我仍然需要时间100%确定。