私は巨大な(70GB)を持っています。一行、文字列(トークン)を置き換えるテキストファイル。<unk>
トークンを別のダミートークンに置き換えたい(手袋の問題)。
私は試しましたsed
:
sed 's/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new
しかし、出力ファイルcorpus.txt.new
には0バイトがあります!
私もPerlを使ってみました。
perl -pe 's/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new
ところで、メモリ不足エラーが発生しました。
小さいファイルの場合、上記の両方のコマンドが機能します。
このようなファイルの文字列をどのように変更できますか? これ関連質問ですが、答えのどれも私には効果的ではありませんでした。
編集する:ファイルを10GB(または他の)サイズのチャンクに分割し、sed
各チャンクに適用してからマージするのはどうですかcat
?馬になる?よりエレガントなソリューションはありますか?
答え1
これらの大容量ファイルにはFlexを使用できます。設定unk.l
:
%%
\<unk\> printf("<raw_unk>");
%%
その後、コンパイルして実行します。
$ flex -o unk.c unk.l
$ cc -o unk -O2 unk.c -lfl
$ unk < corpus.txt > corpus.txt.new
答え2
一般的なテキスト処理ツールは、RAMに収まらない行を処理するように設計されていません。彼らはレコード(行)を読み、それを処理し、結果を出力してから次のレコード(行)に移動する方法で作業する傾向があります。
ASCII文字がファイルに頻繁に表示されるが、または<unk>
には表示されない場合は、<raw_unk>
これをレコード区切り文字として使用できます。ほとんどのツールはカスタムレコード区切り文字を受け入れないため、この文字と改行文字を入れ替えてください。tr
行ではなくバイトを処理するため、レコードサイズを気にしません。有効であると仮定;
:
<corpus.txt tr '\n;' ';\n' |
sed 's/<unk>/<raw_unk>/g' |
tr '\n;' ';\n' >corpus.txt.new
検索テキストで繰り返されず、十分に頻繁に発生することを前提として、検索中のテキストの最初の文字を固定することもできます。ファイルがで始まる場合は、偽の一致を避けるunk>
ためにsedコマンドを変更してくださいsed '2,$ s/…
。
<corpus.txt tr '\n<' '<\n' |
sed 's/^unk>/raw_unk>/g' |
tr '\n<' '<\n' >corpus.txt.new
または最後の文字を使用してください。
<corpus.txt tr '\n>' '>\n' |
sed 's/<unk$/<raw_unk/g' |
tr '\n>' '>\n' >corpus.txt.new
この手法は、sed が改行で終わらないファイルでスムーズに動作すると仮定します。つまり、行を切り捨てたり、最後の改行を追加することなく、行の最後の部分を処理します。 GNU sedで動作します。ファイルの最後の文字をレコード区切り文字として選択できると、移植性の問題を回避できます。
答え3
だからあなたは足りません。物理メモリ(RAM)はファイル全体を一度に保存できますが、64ビットシステムでは十分なRAMがあります。仮想ファイル全体をマップするアドレス空間。この場合、仮想マッピングは簡単なハッキングとして機能します。
必要なタスクはPythonに含まれています。いくつかの迷惑な微妙さがありますが、Cコードを書くのを防ぎます。特にメモリにファイルをコピーしないように注意してください。これにより、この点を完全に無効にすることができます。利点は、エラーレポート(Python「例外」)を無料で受け取ることができることです:)。
#!/usr/bin/python3
# This script takes input from stdin
# (but it must be a regular file, to support mapping it),
# and writes the result to stdout.
search = b'<unk>'
replace = b'<raw_unk>'
import sys
import os
import mmap
# sys.stdout requires str, but we want to write bytes
out_bytes = sys.stdout.buffer
mem = mmap.mmap(sys.stdin.fileno(), 0, access=mmap.ACCESS_READ)
i = mem.find(search)
if i < 0:
sys.exit("Search string not found")
# mmap object subscripts to bytes (making a copy)
# memoryview object subscripts to a memoryview object
# (it implements the buffer protocol).
view = memoryview(mem)
out_bytes.write(view[:i])
out_bytes.write(replace)
out_bytes.write(view[i+len(search):])
答え4
私の考えでは、Cバージョンがより良いパフォーマンスを発揮できるようです。
#include <stdio.h>
#include <string.h>
#define PAT_LEN 5
int main()
{
/* note this is not a general solution. In particular the pattern
* must not have a repeated sequence at the start, so <unk> is fine
* but aardvark is not, because it starts with "a" repeated, and ababc
* is not because it starts with "ab" repeated. */
char pattern[] = "<unk>"; /* set PAT_LEN to length of this */
char replacement[] = "<raw_unk>";
int c;
int i, j;
for (i = 0; (c = getchar()) != EOF;) {
if (c == pattern[i]) {
i++;
if (i == PAT_LEN) {
printf("%s", replacement);
i = 0;
}
} else {
if (i > 0) {
for (j = 0; j < i; j++) {
putchar(pattern[j]);
}
i = 0;
}
if (c == pattern[0]) {
i = 1;
} else {
putchar(c);
}
}
}
/* TODO: fix up end of file if it ends with a part of pattern */
return 0;
}
編集:コメントで提案したように修正されました。また、モードのバグが修正されました<<unk>
。