【问题标题】:Multiline UTF8 regex replace on a large file大文件上的多行 UTF8 正则表达式替换
【发布时间】:2017-11-01 15:56:16
【问题描述】:

我有很大的UTF8 文本文件,其中包含不需要的换行符:并非所有换行符都是坏的,只有其中一些,即那些打破句子的换行符。我想一一浏览文件,加入行并写入新的输出文件。我想使用Python 3(教育原因!)。

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

  1. 我在替换项附近的输出文件中看到垃圾字符。

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

  3. 有没有比我用过的更方便的处理 UTF8 的方法。附加.encode().decode() 很烦人,我似乎是随机添加它们的。我不能以某种方式告诉代码:“一切都在 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("\1 \2".encode(),s))

【问题讨论】:

  • 为什么不能使用逐行解决方案?教育、绩效原因?
  • 我不能使用逐行解决方案,因为正则表达式跨越多行。
  • 如何判断换行符打断了句子?您是否只想在连续有 2 个或更多的地方保留换行符?其他空格呢?如果句子之间的空行上有几个空格,你想保留它们,并将其视为应该保留的中断吗?
  • 在这个问题中,我说我正在使用一个不完美的天真正则表达式。这不是我现在关心的问题。我要解决的问题是列出的三个。假设正则表达式是完美的,并专注于三个主要问题。

标签: python regex python-3.x utf-8 large-files


【解决方案1】:

以下对我有用(在 Windows 上),尽管 read() 调用可能会否定您认为使用 mmap 获得的任何好处。 “垃圾字符”可能是因为您忘记了大多数正则表达式模式字符串上的 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"\1 \2", s.read().decode()) )

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

在下面的代码中,正则表达式模式字符串都带有前缀r b,从而使它们成为byte 模式而不是str 模式。这使得q.sub(br"\1 \2", 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"\1 \2", s).decode() )

【讨论】:

  • 而不是短语s.read().decode(),我似乎可以只使用s。不知道这是否不寻常。但它似乎工作。 r 字符串前缀确实修复了垃圾字符。
  • Tim:只把它放进去是因为在 Python 3.6.3 中它需要在那里以避免TypeError: cannot use a string pattern on a bytes-like object 错误。可能是某种操作系统差异,尽管这让我感到惊讶。你确定你运行了我回答中的所有代码吗?
  • Tim:FWIW,在编码文件上使用mmap 的整个想法似乎有问题。有关某些问题,请参阅Opening memory-mapped file with encoding。但是我认为s.read().decode() 避免了它们,但正如我所说,它可能首先否定使用内存映射文件的好处。
  • 是否可以轻松更改代码以完全避免 mmap,记住文件很大并且正则表达式跨越多行?
  • 假设您的计算机有足够的 RAM 并且没有同时运行太多其他任务,在这种情况下您不需要使用 mmap。我认为没有它的 100Mb 文件应该能够很好地处理,即使您运行的是 32 位操作系统和 32 位 Python。
【解决方案2】:

在 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 上测试

【讨论】:

  • 这消除了所有换行符。我只想摆脱正则表达式中指定的那些=那些破坏英文句子的换行符。其他换行符很好。我现在修改了问题以加强这一点。 – 蒂姆 1 小时前
猜你喜欢
  • 2018-12-19
  • 1970-01-01
  • 1970-01-01
  • 2020-01-26
  • 2013-01-12
  • 2017-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多