在Python中使用mmap和re.findall搜索一个大文件时出现MemoryError。

问题描述 投票:2回答:1

我想用re实现几行python,首先操作一个字符串,然后用这个字符串作为regex搜索。我的字符串有 *在他们中间,即。ab***cd*的任意长度。这样做的目的是在文档中进行regex搜索,提取任何符合起始和结束字符的行,中间有任意数量的字符,即ab12345cd,abbbcd,ab_fghfghfghcd,都是正向匹配。负匹配的例子。1abcd, agcd, bb111cd.

我想出了以下的重构码 [\s\S]*? 输入,而不是 *'s. 所以,我想从一个例子字符串中获取 ab***cd^ab[\s\S]*?cd然后,我将用它来搜索一个文件的regex。

然后,我想在mmap中打开文件,使用regex搜索,然后将匹配的文件保存到文件中。

import re
import mmap 

def file_len(fname):
    with open(fname) as f:
        for i, l in enumerate(f):
            pass
    return i + 1

def searchFile(list_txt, raw_str):
    search="^"+raw_str #add regex ^ newline operator
    search_rgx=re.sub(r'\*+',r'[\\s\\S]*?',search) #replace * with regex function

    #search file
    with open(list_txt, 'r+') as f: 
        data = mmap.mmap(f.fileno(), 0)
        results = re.findall(bytes(search_rgx,encoding="utf-8"),data, re.MULTILINE)

    #save results
    f1 = open('results.txt', 'w+b')
    results_bin = b'\n'.join(results)
    f1.write(results_bin)
    f1.close()

    print("Found "+str(file_len("results.txt"))+" results")

searchFile("largelist.txt","ab**cd")

现在,这在小文件中可以正常工作。但是当文件变大的时候(1gb的文本),我得到了这个错误。

Traceback (most recent call last):
  File "c:\Programming\test.py", line 27, in <module>
    searchFile("largelist.txt","ab**cd")
  File "c:\Programming\test.py", line 21, in searchFile
    results_bin = b'\n'.join(results)
MemoryError

首先,谁能帮我稍微优化一下代码?我是不是做了什么严重的错误?我使用了mmap,因为我知道我想看大文件,我想一行一行地读文件,而不是一次读完(因此有人建议使用mmap)。

也有人告诉我可以看看pandas库,以获得更多的数据操作。pandas的可以代替mmap吗?

谢谢你的帮助。你可以看出,我对python很陌生,所以很感激任何帮助。

python pandas mmap large-files re
1个回答
1
投票

那这样呢?在这种情况下,你需要的是一个用字符串表示的所有行的列表。下面的代码模拟了这种情况,产生了一个字符串列表。

import io

longstring = """ab12345cd
abbbcd
ab_fghfghfghcd
1abcd
agcd
bb111cd
"""

list_of_strings = io.StringIO(longstring).read().splitlines()
list_of_strings

输出

['ab12345cd', 'abbbcd', 'ab_fghfghfghcd', '1abcd', 'agcd', 'bb111cd']

这是最重要的部分

s = pd.Series(list_of_strings)
s[s.str.match('^ab[\s\S]*?cd')]

产出

0         ab12345cd
1            abbbcd
2    ab_fghfghfghcd
dtype: object

编辑2: 试试这个: (我不明白你为什么要把它作为一个函数,但我已经这样做了,因为你在评论中做了什么。)

def newsearch(filename):
    with open(filename, 'r', encoding="utf-8") as f:
        list_of_strings = f.read().splitlines()
    s = pd.Series(list_of_strings)
    s = s[s.str.match('^ab[\s\S]*?cd')]
    s.to_csv('output.txt', header=False, index=False)

newsearch('list.txt')

基于分块的方法

import os

def newsearch(filename):
    outpath = 'output.txt'
    if os.path.exists(outpath):
        os.remove(outpath)
    for chunk in pd.read_csv(filename, sep='|', header=None, chunksize=10**6):
        chunk = chunk[chunk[0].str.match('^ab[\s\S]*?cd')]
        chunk[0].to_csv(outpath, index=False, header=False, mode='a')

newsearch('list.txt')

一个ask方法

import dask.dataframe as dd

def newsearch(filename):
    chunk = dd.read_csv(filename, header=None, blocksize=25e6)
    chunk = chunk[chunk[0].str.match('^ab[\s\S]*?cd')]
    chunk[0].to_csv('output.txt', index=False, header=False, single_file = True)

newsearch('list.txt')

2
投票

你正在进行逐行处理,所以你要避免在内存中积累数据。常规的文件读写在这里应该可以很好地工作。mmap 是由虚拟内存支持的,但当你读取它时,它必须变成真实的内存。积累的结果是 findall 也是一个内存占用者。试试这个作为替代方案。

import re

# buffer to 1Meg but any effect would be modest
MEG = 2**20

def searchFile(filename, raw_str):
    # extract start and end from "ab***cd"
    startswith, endswith = re.match(r"([^\*]+)\*+?([^\*]+)", raw_str).groups()
    with open(filename, buffering=MEG) as in_f, open("results.txt", "w", buffering=MEG) as out_f:
        for line in in_f:
            stripped = line.strip()
            if stripped.startswith(startswith) and stripped.endswith(endswith):
                out_f.write(line)

# write test file

test_txt = """ab12345cd
abbbcd
ab_fghfghfghcd
1abcd
agcd
bb111cd
"""

want = """ab12345cd
abbbcd
ab_fghfghfghcd
"""

open("test.txt", "w").write(test_txt)

searchFile("test.txt", "ab**cd")

result = open("results.txt").read()
print(result == want)

2
投票

我不知道你认为用这个方法打开输入文件会有什么好处 mmap但由于每个必须匹配的字符串都是以换行来定界的 (根据你的评论),我将使用下面的方法 (注意,它是 Python,但特意保留为伪代码)。

with open(input_file_path, "r") as input_file:
  with open(output_file_path, "x" as output_file:
    for line in input_file:
      if is_match(line):
        print(line, file=output_file)

可能会调整 endline 参数的 print 函数来满足您的需求。

这样,结果在生成时就会被写入,你就可以避免有大量的 results 而且,你不需要关注换行,只需要关注每行是否匹配。只需要关注每行是否匹配。

© www.soinside.com 2019 - 2024. All rights reserved.