각 행의 정확한 복제본이 약 100개 포함된 거대한(최대 2GiB) 텍스트 파일이 있습니다(제 경우에는 파일이 CSV와 유사한 데이터 테이블이므로 쓸모가 없습니다).
나에게 필요한 것은 원래 시퀀스 순서를 유지하면서(가급적이지만 상당한 성능 향상을 위해 희생될 수 있음) 모든 중복 항목을 제거하는 것입니다. 결과적으로 각 행은 고유합니다. 100개의 동일한 줄이 있는 경우(일반적으로 중복된 줄은 파일 전체에 퍼져 있으며 이웃이 되지 않음) 그 중 하나만 남게 됩니다.
저는 이 기능을 구현하기 위해 Scala로 프로그램을 작성했습니다(Scala를 모른다면 Java 사용을 고려해보세요). 하지만 이 작업을 더 빠르게 수행할 수 있는 C로 작성된 더 빠른 기본 도구가 있을까요?
업데이트: awk '!seen[$0]++' filename
이 솔루션은 파일이 2GiB 이하에 가까우면 제대로 작동하는 것처럼 보였지만 이제 8GiB 파일을 정리하려고 하면 더 이상 작동하지 않습니다. 4GiB RAM이 장착된 Mac과 4GiB RAM 및 6GiB 스왑이 장착된 64비트 Windows 7 PC에서는 메모리 부족으로 끝이 없어 보입니다. 이러한 경험을 고려할 때 저는 4GiB RAM을 갖춘 Linux에서 시도하는 것에 열광하지 않았습니다.
답변1
awk
#bash(Freenode)에서 볼 수 있는 솔루션:
awk '!seen[$0]++' filename
파일을 그 자리에서 편집하려면 다음 명령을 사용할 수 있습니다(이 확장을 구현하는 GNU awk 버전을 사용하는 경우).
awk -i inplace '!seen[$0]++' filename
답변2
실행을 제외하고 많은 메모리를 필요로 하지 않는 표준 유틸리티를 사용하는 간단한(명백하지는 않지만) 방법이 있으며 sort
대부분의 구현에는 대용량 파일에 대한 특정 최적화(좋은 외부 정렬 알고리즘)가 있습니다. 이 방법의 장점은 전용 유틸리티 내의 모든 라인에 대해서만 루프를 수행하고 해석된 언어 내의 모든 라인에 대해서는 루프를 수행하지 않는다는 것입니다.
<input nl -b a -s : | # number the lines
sort -t : -k 2 -u | # sort and uniquify ignoring the line numbers
sort -t : -k 1n | # sort according to the line numbers
cut -d : -f 2- >output # remove the line numbers
모든 줄이 공백이 아닌 문자로 시작하는 경우 일부 옵션을 생략할 수 있습니다.
<input nl | sort -k 2 -u | sort -k 1n | cut -f 2- >output
많은 양의 반복의 경우 각 행의 단일 복사본만 메모리에 저장하면 되는 방법이 더 나은 성능을 발휘합니다. 약간의 설명 오버헤드와 함께 매우 깔끔한 awk 스크립트가 있습니다(이미게시자: enzotib):
<input awk '!seen[$0]++'
덜 간결하게: !seen[$0] {print} {seen[$0] += 1}
즉, 아직 표시되지 않은 경우 현재 줄을 인쇄한 다음 seen
해당 줄에 대한 카운터를 증가시킵니다(초기화되지 않은 변수 또는 배열 요소의 숫자 값은 0입니다).
긴 줄의 경우 각 줄의 검열할 수 없는 체크섬(예: 암호화 다이제스트)만 유지하여 메모리를 절약할 수 있습니다. 예를 들어 SHA-1을 사용하면 각 라인에 20바이트와 지속적인 오버헤드만 필요합니다. 그러나 다이제스트 계산은 상당히 느립니다. CPU가 빠르고(특히 다이제스트를 계산하기 위한 하드웨어 가속기가 있는 CPU) 파일 크기에 비해 메모리가 많지 않고 줄이 충분히 긴 경우에만 이 접근 방식이 효과적입니다. 줄당 체크섬을 계산할 수 있는 기본 유틸리티는 없습니다. Perl/Python/Ruby/...의 해석 오버헤드를 감수하거나 전용 컴파일러를 작성해야 합니다.
<input perl -MDigest::MD5 -ne '$seen{Digest::MD5::md5($_)}++ or print' >output
답변3
sort -u big-csv-file.csv > duplicates-removed.csv
출력 파일이 정렬됩니다.
답변4
gawk -i inplace '!a[$0]++' SOME_FILE [SOME_OTHER_FILES...]
이 명령은 순서를 유지하면서 중복된 줄을 필터링하고 파일을 적절한 위치에 저장합니다.
모든 고유 라인의 캐시를 유지하고 각 라인을 한 번만 인쇄하여 이 작업을 수행합니다.
정확한 알고리즘은 다음과 같이 분류될 수 있습니다.
- 현재 줄을 변수에 저장
$0
- 연관 배열에
a
키가 있는지 확인하고$0
, 그렇지 않은 경우 키를 생성하고 해당 값을 다음과 같이 초기화합니다.0
- 키 값을 비교하고
0
true인 경우 현재 줄을 인쇄합니다. - 열쇠의 가치를 높여라
1
- 다음 행을 가져와서 1단계로 이동합니다. EOF에 도달할 때까지
또는 의사코드로:
while read $line
do
$0 := $line
if not a.has_key($0) :
a[$0] := 0
if a[$0] == 0 :
print($line)
a[$0] := a[$0] + 1
done
참고: 이 명령에는 2013년 이상의 GNU AWK 버전 4.1이 필요합니다.