fileAには約100,000個の文字列が含まれています(人名のみa-zA-Z
)。
fileBには約1億行があります。
プログラム
プログラムは2つだけです。
- 文字列を単一点に置き換える
- 文字列を同じ長さの点に置き換える
演算
for each lineB in fileB do
for each lineA in fileA do
if lineA matches with lineB; then
replace the match in lineB with dots
append the modified lineB' to file "res-length" or "res-single", depending on the program
fi
done
done
直接的な解決策は非常に遅いです。
一致は大文字と小文字を区別する必要があります。
他のLinuxアプリケーション(gawkなど)も追加でインストールできます。
はい
$ cat fileA
agnes
Ari
Vika
$ cat fileB
12vika1991
ariagnes#!
ari45
lera56er
出力は各プログラムごとに1つずつ、2つのファイルでなければなりません。
$ cat res-single # replace a string with a single dot
12.1991
.agnes#!
ari.#!
.45
$ cat res-length # replace a string with dots of the same length
12...1991
...agnes#!
ari.....#!
...45
このタスクの単純化されたバージョンには独自の出力が必要です。最初マッチ。したがって、プログラム#2の代わりに...agnes#!
出力ari.....#!
だけで十分です。ari.....#!
単純化された作業アルゴリズム
for each lineB in fileB do
find the first lineA in fileA that matches lineB
if lineA is found; then
replace the match in lineB with dots
append the modified lineB' to file "res-length" or "res-single", depending on the program
fi
done
Pythonの実装
def create_masks(wordlist=WordListDefault.TOP1M.path, replace_char='.'):
# fileA lowercase
names = PATTERNS_PATH.read_text().splitlines()
masks_length = []
masks_single = []
with codecs.open(wordlist, 'r', encoding='utf-8', errors='ignore') as infile:
for line in infile:
line_lower = line.lower()
for name in names:
i = line_lower.find(name)
if i != -1:
ml = f"{line[:i]}{replace_char * len(name)}{line[i + len(name):]}"
ms = f"{line[:i]}{replace_char}{line[i + len(name):]}"
masks_length.append(ml)
masks_single.append(ms)
with open(MASKS_LENGTH, 'w') as f:
f.writelines(masks_length)
with open(MASKS_SINGLE, 'w') as f:
f.writelines(masks_single)
if __name__ == '__main__':
create_masks()
160万個のファイルAと1,000個のファイルBの場合、約3分かかり、わずか10秒に短縮されましたgrep -iF -f fileA fileB > fileB.filtered
。
@Ned64さんの言葉が正しいです。最速の方法は単純なCです。これはこのフォーラムのトピックではありません。
現在のPython実装では、fileBの2B行とfileAの35k文字列を処理するには52日かかります。純粋なCが1時間以内にこれを実行できるかどうかはもうわかりません。 CUDAが実行可能なアプローチかどうか疑問に思います。
ベストアンサー1
$ cat tst.awk
BEGIN {
dots = sprintf("%*s",1000,"")
gsub(/ /,".",dots)
resSingle = "res-single"
resLength = "res-length"
}
{ lc = tolower($0) }
NR==FNR {
lgth = length($0)
str2lgth[lc] = lgth
str2dots[lc] = substr(dots,1,lgth)
next
}
{
for (str in str2lgth) {
if ( s=index(lc,str) ) {
bef = substr($0,1,s-1)
aft = substr($0,s+str2lgth[str])
print bef "." aft > resSingle
print bef str2dots[str] aft > resLength
}
}
}
。
$ awk -f tst.awk fileA fileB
$ cat res-single
12.1991
ari.#!
.agnes#!
.45
$ cat res-length
12....1991
ari.....#!
...agnes#!
...45
上記は、fileAに1000文字を超える行がないと仮定しています。これが間違っている場合は、より大きな数字を選択するか、必要に応じてコードを追加して計算できます。また、fileAの行がfileBで見つかった順序に興味がなく、正規表現比較ではなく文字列比較を実行したいとします。どちらも欲しいものではない場合はマイナーな調整です。
以下のコメントに応じて編集してください。 fileAで行の最大長を静的に定義できない場合(100,000文字を超えてはいけませんか?)、上記の内容を修正して最大値を計算し、fileAの行を変更する方法は次のとおりです。すべて小文字です:
NR==FNR {
lgth = length($0)
str2lgth[$0] = lgth
maxLgth = (lgth > maxLgth ? lgth : maxLgth)
next
}
FNR==1 {
dots = sprintf("%*s",maxLgth,"")
gsub(/ /,".",dots)
for ( str in str2lgth ) {
str2dots[str] = substr(dots,1,str2lgth[str])
}
resSingle = "res-single"
resLength = "res-length"
}
{
lc = tolower($0)
for (str in str2lgth) {
if ( s=index(lc,str) ) {
bef = substr($0,1,s-1)
aft = substr($0,s+str2lgth[str])
print bef "." aft > resSingle
print bef str2dots[str] aft > resLength
}
}
}