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

Python - How to search a string in a large file

我有一个大文件,其中可以包含 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 脚本更好的方法来获得这些结果?

提前致谢!

先把现有的收集起来,然后寻找缺失的。

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 中它也会挂起。

我建议您只需逐行阅读输入文件并解析每一行的文件编号。然后将该文件号用作最初设置为 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]

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

  1. 内存映射文件。

  2. 每个数字

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

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

此处的解决方案是逐行通过文件一次。如果要搜索大量数字,则应预编译正则表达式而不是每个数字编译一次。要一次获得所有数字,您可以对所有数字进行 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

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