我有一个巨大的(70GB),一行文本文件,我想替换其中的一个字符串(令牌)。我想<unk>
用另一个虚拟令牌(手套问题)替换令牌。
我试过sed
:
sed 's/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new
但输出文件corpus.txt.new
有零字节!
我也尝试使用 perl:
perl -pe 's/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new
但我遇到了内存不足的错误。
对于较小的文件,上述两个命令都有效。
如何替换字符串是这样的文件? 这是一个相关的问题,但没有一个答案对我有用。
编辑:如何将文件分成 10GB(或其他)的块并应用sed
到每个块上,然后将它们合并cat
?那有意义吗?有没有更优雅的解决方案?
对于这么大的文件,一种可能是 Flex。让
unk.l
:然后编译并执行:
通常的文本处理工具并非旨在处理不适合 RAM 的行。他们倾向于通过读取一条记录(一行)、操作它并输出结果,然后继续处理下一条记录(行)来工作。
如果有一个 ASCII 字符经常出现在文件中而没有出现在
<unk>
or<raw_unk>
中,那么您可以使用它作为记录分隔符。由于大多数工具不允许自定义记录分隔符,因此在该字符和换行符之间进行交换。tr
处理字节,而不是行,因此它不关心任何记录大小。假设;
有效:您还可以锚定在您正在搜索的文本的第一个字符上,假设它在搜索文本中没有重复并且它出现的频率足够高。如果文件可能以 开头
unk>
,请将 sed 命令更改为sed '2,$ s/…
以避免虚假匹配。或者,使用最后一个字符。
请注意,此技术假定 sed 对不以换行符结尾的文件无缝运行,即它处理最后部分行而不截断它并且不附加最终换行符。它适用于 GNU sed。如果您可以选择文件的最后一个字符作为记录分隔符,您将避免任何可移植性问题。
因此,您没有足够的物理内存 (RAM) 来一次保存整个文件,但在 64 位系统上,您有足够的虚拟地址空间来映射整个文件。在这种情况下,虚拟映射可以用作简单的 hack。
必要的操作都包含在 Python 中。有一些令人讨厌的细微之处,但它确实避免了编写 C 代码。特别是,需要注意避免将文件复制到内存中,这将完全破坏这一点。从好的方面来说,您可以免费获得错误报告(python“异常”):)。
replace
mariadb-server/mysql-server 包中有一个实用程序。它替换简单的字符串(不是正则表达式),并且不像 grep/sed/awkreplace
不关心\n
and\0
。任何输入文件(我的机器上大约 400kb)的内存消耗都是恒定的。当然你不需要运行 mysql 服务器来使用
replace
,它只是在 Fedora 中以这种方式打包的。其他发行版/操作系统可能会将其单独打包。我认为 C 版本的性能可能会更好:
编辑:根据评论中的建议进行修改。还修复了该模式的错误
<<unk>
。GNU
grep
可以显示“二进制”文件中匹配的偏移量,而无需将整行读入内存。然后,您可以使用dd
最多读取此偏移量,跳过匹配项,然后继续从文件复制。为了速度,我将它
dd
分成块大小为 1048576 的大读取和一次 1 字节的较小读取,但是对于这么大的文件,这个操作仍然会有点慢。grep
例如,输出是 ,并且13977:<unk>
通过读取到变量offset
和,在冒号上将其拆分pattern
。我们必须跟踪pos
已经从文件中复制了多少字节。这是另一个可能比其他选项执行得更好的单个 UNIX 命令行,因为您可以“寻找”执行良好的“块大小”。为了使它健壮,您需要知道每个 X 字符中至少有一个空格,其中 X 是您的任意“块大小”。在下面的示例中,我选择了 1024 个字符的“块大小”。
在这里, fold最多可以抓取1024 个字节,但是 -s 确保它在空间上中断,如果自上次中断后至少有一个。
sed 命令是您的命令,可以执行您所期望的操作。
然后 tr 命令将“展开”文件,将插入的换行符转换为空。
您应该考虑尝试更大的块大小,看看它是否执行得更快。对于 fold 的 -w 选项,您可以尝试使用 10240 和 102400 和 1048576 而不是 1024。
这是一个将所有 N 转换为小写的每个步骤分解的示例:
如果有文件,您将需要在文件的最后添加一个换行符,因为 tr 命令将删除它。
使用
perl
管理自己的缓冲区
您可以使用
IO::Handle
's管理默认缓冲区,也可以使用和setvbuf
管理您自己的缓冲区。检查并获取更多信息,基本上他们跳过缓冲 io。sysread
syswrite
perldoc -f sysread
perldoc -f syswrite
这里我们滚动我们自己的缓冲区 IO,但是我们在 1024 字节上手动任意执行。我们还为 RW 打开文件,因此我们一次在同一个 FH 上完成所有操作。
如果你要走这条路
<unk>
和<raw_unk>
是相同的字节大小。CHUNKSIZE
如果要替换超过 1 个字节,您可能需要确保我们的缓冲方法不会越界。你可以试试bbe(二进制块编辑器),一个“
sed
二进制文件”。我在一个没有字符的 7GB 文本文件上使用它取得了很好的成功
EOL
,用不同长度的一个字符串替换了多次出现的字符串。在没有尝试任何优化的情况下,它给出了 > 50MB/s 的平均处理吞吐量。这是一个执行任务的小型 Go 程序 (
unk.go
):只需构建它
go build unk.go
并以./unk <input >output
.编辑:
抱歉,我没有读到所有内容都在一行中,所以我现在尝试逐个字符地读取文件。
编辑二:
应用与 C 程序相同的修复程序。