Python - 如何在大文件中搜索字符串

时间:2017-09-28 19:03:13

标签: python python-3.x file-search

我有一个大文件,可以包含file_+0.txt, file_[]1.txt, file_~8.txt等字符串。

我希望找到丢失的files_*.txt,直到某个数字。

例如,如果我给出下面的文件和数字5,它应该告诉缺少的文件是1 and 4

asdffile_[0.txtsadfe
asqwffile_~2.txtsafwe
awedffile_[]2.txtsdfwe
qwefile_*0.txtsade
zsffile_+3.txtsadwe

我编写了一个Python脚本,我可以给它提供文件路径和一个数字,它将为我提供在该数字之前缺少的所有文件名。

我的程序适用于小文件。但是,当我提供一个大文件(12MB),文件编号可以达到10000时,它就会挂起。

这是我目前的Python代码

#! /usr/bin/env/python
import mmap
import re

def main():
    filePath = input("Enter file path: ")
    endFileNum = input("Enter end file number: ")
    print(filePath)
    print(endFileNum)
    filesMissing = []
    filesPresent = []
    f = open(filePath, 'rb', 0)
    s = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
    for x in range(int(endFileNum)):
        myRegex = r'(.*)file(.*)' + re.escape(str(x)) + r'\.txt'
        myRegex = bytes(myRegex, 'utf-8')
        if re.search(myRegex, s):
            filesPresent.append(x)
        else:
            filesMissing.append(x)
    #print(filesPresent)
    print(filesMissing)

if __name__ == "__main__":
    main()

当我提供一个12MB的文件时,输出会挂起,该文件可以包含0到9999的文件

$python findFileNumbers.py
Enter file path: abc.log
Enter end file number: 10000

小文件的输出(与上例相同)

$python findFileNumbers.py
Enter file path: sample.log
Enter end file number: 5
[0, 2, 3]
[1, 4]
  1. 如何使这个工作适用于大文件?
  2. 有没有更好的方法来获取这些结果而不是Python脚本?
  3. 提前致谢!

3 个答案:

答案 0 :(得分:2)

首先收集一组中现有的,然后寻找遗漏的那些。

my_regex = re.compile('.*file.*(\d+)\.txt.*')
present_ones = set()
for line in open(filepath):
    match = my_regex.match(line)
    if match:
       present_ones.add(int(match.group(1)))
for num in range(...):
    if num not in present_ones:
        print("Missing" + num)

你的原因因为你要浏览每个号码的整个文件而挂起。即12MB * 10000 = 120GB脚本将通过120GB,因此即使你在mmap中使用它也会挂起。

答案 1 :(得分:1)

我建议您只需逐行读取输入文件并解析每行的文件编号。然后使用该文件号作为布尔数组的索引,最初设置为False。

您不需要执行任何需要将文件存入内存的处理。这种方法适用于非常大的文件。

#~ import mmap
import re
import numpy as np

def main():
    #~ filePath = input("Enter file path: ")
    filePath = 'filenames.txt'
    #~ endFileNum = input("Enter end file number: ")
    endFileNum = 5
    print(filePath)
    print(endFileNum)
    found = np.zeros(1+endFileNum, dtype=bool)
    patt = re.compile(r'[^\d]+(\d+)')
    with open(filePath) as f:
        for line in f.readlines():
            r = patt.search(line).groups(0)[0]
            if r:
                found[int(r)]=True
    print (found)

    #~ filesMissing = []
    #~ filesPresent = []
    #~ files = np.zeros[endFileNum, dtype=bool]
    #~ f = open(filePath, 'rb', 0)
    #~ s = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
    #~ for x in range(int(endFileNum)):
        #~ myRegex = r'(.*)file(.*)' + re.escape(str(x)) + r'\.txt'
        #~ myRegex = bytes(myRegex, 'utf-8')
        #~ if re.search(myRegex, s):
            #~ filesPresent.append(x)
        #~ else:
            #~ filesMissing.append(x)
    #print(filesPresent)
    #~ print(filesMissing)

if __name__ == "__main__":
    main()

这会产生以下结果:filesPresentfilesMissing可以轻松恢复。

filenames.txt
5
[ True False  True  True False False]

答案 2 :(得分:1)

让我们来看看你在这里做了什么:

  1. 内存映射文件。
  2. 每个号码

    一个。编译该数字的正则表达式 湾在整个文件中搜索正则表达式。

  3. 对于大数字来说这是非常低效的。虽然内存映射为文件提供了类似字符串的接口,但它并不神奇。您仍然有文件的加载块在其中移动。同时,您正在为每个正则表达式传递一个可能在整个文件中的传递。正则表达式匹配也很昂贵。

    这里的解决方案是逐行遍历文件。如果要搜索的数字很大,则应预编译正则表达式,而不是每个数字编译一次。要一次性获取所有数字,您可以将set所有数字设置为所需数字,称为“缺失”,并将空set称为“找到”。每当遇到带数字的行时,您都会将数字从“缺失”移动到“找到”。

    以下是一个示例实现:

    filePath = input("Enter file path: ")
    endFileNum = int(input("Enter end file number: "))
    missing = set(range(endFileNum))
    found = set()
    regex = re.compile(r'file_.*?(\d+)\.txt')
    with open(filePath) as file:
        for line in file:
            for match in regex.finditer(line)
                num = int(match.groups(1))
                if num < endFileNum:
                    found.add(num)
    missing -= found
    

    请注意,正则表达式在.*?之后使用reluctant quantifier file_。在查找数字之前,这将匹配尽可能少的字符。如果您有默认的贪心量词.*,则一行中的多个数字只会匹配最后一个。