多行 UTF8 正则表达式替换大文件

Multiline UTF8 regex replace on a large file

我有大型 UTF8 文本文件,其中包含不需要的 linebreaks:not 所有换行符都不好,只有其中一些,即那些中断句子的。我想一个一个地浏览文件,加入行并写入新的输出文件。我想用Python 3(教育原因!)。

我在小文件上测试了下面的代码。它几乎可以工作。但是我有三个问题。

  1. 我在替换项附近的输出文件中得到乱码。

  2. 对于较大的文件(大约 100Mb)是否有更好的做法?我大概无法使用 this 等逐行解决方案。我认为使用 MMAP 有助于阅读部分,但写作部分呢?

  3. 有没有比我用过更方便的处理UTF8的方法。附加 .encode().decode() 很烦人,我似乎是随机添加它们。我不能以某种方式告诉代码:"everything is in UTF8"

(我正在通过一个天真的正则表达式定位不需要的换行符。我知道它可能会更好,但这不是我目前关心的问题。)

#!/opt/local/bin/python3.6
from mmap import mmap, ACCESS_READ
from re import compile,MULTILINE

q=compile('([a-z])\n+([a-z])'.encode(),MULTILINE)
with open("infile", 'rb', 0) as file, open("outfile", "wb") as outfile, \
mmap(file.fileno(), 0, access=ACCESS_READ) as s:
    outfile.write(q.sub(" ".encode(),s))

在 python3 中实现目标的一种非常简单的方法是

with open(r'c:\temp\infile.txt', 'r', encoding='utf-8') as f1, \
     open(r'c:\temp\outfile.txt', "w", encoding='utf-8' ) as o1:
    for line in f1.read():
        o1.write(line.strip('\n'))

不需要正则表达式。对于 100 MB 的文件,逐行方法实际上非常有效。

测试于 windows 7

以下对我有用(在 Windows 上),尽管 read() 调用可能会否定您认为使用 mmap 获得的任何好处。 "rubbish characters" 可能是因为您忘记了大多数正则表达式模式字符串上的 r 字符串前缀(通常需要)。顺便说一句,您的正则表达式模式似乎有效。

from mmap import mmap, ACCESS_READ
from re import compile, MULTILINE

q = compile(r'([a-z])\r\n+([a-z])', MULTILINE)

with open("regex_subst.txt", 'r', encoding='utf-8') as file, \
     open("outfile.txt", "w", encoding='utf-8') as outfile, \
     mmap(file.fileno(), 0, access=ACCESS_READ) as s:

    outfile.write( q.sub(r" ", s.read().decode()) )

这里有一个略有不同的方法,它不调用 read() 但仍然有效。 re 模块可用于字符串 字节,只要您传递给它的值始终是一种类型或另一种类型。

在下面的代码中,正则表达式模式字符串都以字母 r b 为前缀,因此使它们成为 byte模式而不是 str 模式。这使得 q.sub(br" ", s) 不会生成 TypeError: cannot use a string pattern on a bytes-like object 错误。但是,在将结果写入 UTF8 编码的输出文件之前,必须首先对替换的字节字符串结果进行显式解码,如图所示。

from mmap import mmap, ACCESS_READ
from re import compile, MULTILINE

q = compile(br'([a-z])\r\n+([a-z])', MULTILINE)

with open("regex_subst.txt", 'r', encoding='utf-8') as file, \
     open("outfile.txt", "w", encoding='utf-8') as outfile, \
     mmap(file.fileno(), 0, access=ACCESS_READ) as s:

    outfile.write( q.sub(br" ", s).decode() )