fileA에는 약 100,000개의 문자열이 포함되어 있습니다(사람 이름만 해당 a-zA-Z
).
fileB에는 약 1억 줄이 있습니다.
프로그램
프로그램은 두 가지뿐입니다.
- 문자열을 단일 점으로 교체
- 문자열을 같은 길이의 점으로 교체
연산
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
출력은 각 프로그램당 하나씩, 두 개의 파일이어야 합니다.
$ 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
파이썬 구현
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가 한 시간 안에 이 작업을 수행할 수 있는지 더 이상 확신할 수 없습니다. 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
}
}
}
답변2
여기에서는 간단한 Perl 기반 접근 방식을 사용할 수 있습니다.
방법:
키가 fileA의 소문자 라인(줄 바꿈 없음)이고 값이 동등한 포인트인 해시 %h를 채웁니다.
그런 다음 fileB의 각 행에 대해 해시 %h의 키가 대소문자를 구분하지 않고 존재하는지 테스트합니다. 그렇다면 사전 일치, 일치 및 사후 일치 데이터를 res-single 및 res-length 파일로 인쇄합니다. 첫 번째 일치 항목만 원하는 경우 "마지막" 문의 주석을 해제하세요.
$ perl -Mautodie -lne '
BEGIN {
open *{"FH$_"}, ">", qw[res-single res-length][$_] for 0..1;
do{
local @ARGV = pop;
$h{do{chomp;lc;}} = s/././gr =~ tr/\n//dr while <>;
@h = keys %h;
};
}
for my $h ( @h ) {
if ( /\Q$h/pi ) {
my($p, $q) = (${^PREMATCH}, ${^POSTMATCH});
print {*{"FH$_"}} $p, (".", $h{$h})[$_], $q for 0..1;
#last;
}
}
' fileB fileA
$ more res-*
::::::::::::::
res-length
::::::::::::::
12....1991
ari.....#!
...agnes#!
...45
::::::::::::::
res-single
::::::::::::::
12.1991
ari.#!
.agnes#!
.45
답변3
최적화된 C 솔루션https://github.com/dizcza/people-names-as-passwords/blob/master/src/create_masks.c
나는 trie 데이터 구조를 사용했고 이를 통해 12분 안에 2B 행 fileB
과 43,000행을 구문 분석할 수 있었습니다!fileA
귀하의 의견에 감사드립니다.