텍스트 파일에서 중복 줄을 제거하는 방법은 무엇입니까?

텍스트 파일에서 중복 줄을 제거하는 방법은 무엇입니까?

각 행의 정확한 복제본이 약 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...]

이 명령은 순서를 유지하면서 중복된 줄을 필터링하고 파일을 적절한 위치에 저장합니다.

모든 고유 라인의 캐시를 유지하고 각 라인을 한 번만 인쇄하여 이 작업을 수행합니다.

정확한 알고리즘은 다음과 같이 분류될 수 있습니다.

  1. 현재 줄을 변수에 저장$0
  2. 연관 배열에 a키가 있는지 확인하고 $0, 그렇지 않은 경우 키를 생성하고 해당 값을 다음과 같이 초기화합니다.0
  3. 키 값을 비교하고 0true인 경우 현재 줄을 인쇄합니다.
  4. 열쇠의 가치를 높여라1
  5. 다음 행을 가져와서 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이 필요합니다.

관련 정보