python - "grep"大文件的最快方法

标签 python python-2.7

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

由于这是一项重复性任务,我需要它尽快完成。最快的获取方式是什么?

我想过4个策略:

  • 用 Python 读取整个文件并搜索正则表达式(方法 1)
  • 只读取文件的最后 15,000 个字节并搜索正则表达式(方法 2)
  • 对 grep 进行系统调用(method_3)
  • 在跟踪最后 200 行后对 grep 进行系统调用(方法 4)

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

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 |
+----------+-----------+-----------+

最佳答案

您也可以考虑像这样使用内存映射(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 的开销相比,差异可能消失)
  • 在函数开头编译你的正则表达式可能会有所不同

关于python - "grep"大文件的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40868592/

相关文章:

python - 包含同名类的 Python 模块在导入时如何工作?

python - Python 脚本中的多线程

Python 映射 lambda 过滤器帮助使用 IF

python - 创建具有共享轴的 python seaborn 热图的 2x2 网格

python - 在 Mac OSX 上安装 pandas 和 numpy 时出现问题

python-2.7 - 关于数组和 if,满足质量标准

python - 在 Pandas 中,如何在单个单元格中设置数字格式?

python - pandas 应用并分配给多列

python - 获取负数的 2 补码的十六进制

mysql - Python MySQLdb - 围绕 SQL 注入(inject)代码编程