텍스트 파일에서 중복 줄을 제거하는 방법?


126

내 거대한 (최대 2 GiB) 텍스트 파일에는 모든 줄의 약 100 정확한 사본이 포함되어 있습니다 (제 경우에는 CSV와 같은 데이터 테이블이므로 쓸모가 없습니다).

내가 필요로하는 것은 원래 순서를 유지하면서 모든 반복을 제거하는 것이 좋습니다 (바람직하게는 성능을 크게 향상시키기 위해 희생 될 수 있음). 결과적으로 각 라인은 고유해야합니다. 100 개의 동일한 행이있는 경우 (일반적으로 복제본이 파일에 분산되고 이웃이 아님) 남은 종류 중 하나만 있어야합니다.

이것을 구현하기 위해 스칼라로 프로그램을 작성했습니다 (스칼라에 대해 모른다면 Java로 간주하십시오). 그러나 더 빠른 C 작성 네이티브 도구가 더 빠를 수 있습니까?

업데이트 : awk '!seen[$0]++' filename파일이 2 GiB 이하인 경우 솔루션이 제대로 작동하는 것처럼 보였지만 이제는 8 GiB 파일을 정리할 때 더 이상 작동하지 않습니다. 4GiB RAM이 장착 된 Mac과 4GiB RAM이 장착 된 64 비트 Windows 7 PC 및 6GiB 스왑에서 메모리가 부족한 것으로 보입니다. 그리고 나는이 경험을 감안할 때 4GiB RAM을 가진 리눅스에서 시도하는 것에 대해 열의를 느끼지 않습니다.


이것은 당신의 순서를 파괴하지만, 당신은 sort -u를 시도
— 0x7c0

5
C는 종종 Java보다 훨씬 빠르지 않으며, 지금 (순서대로) 실행하고 있다면 여기에 답을 얻고 구현하고 실행을 마치기 전에 완료 될 가능성이 높습니다. 순서가, sort -u아마 빨라집니다.
— 케빈

답변:


215

awk#bash (Freenode의)에 본 솔루션 :

awk '!seen[$0]++' filename

1
방금 2G 파일로 시도했는데 내 노트북에서 3 분이 걸렸습니다. 나쁘지 않다. 나는 또한 uniq 파일 이름을 시도 | awk '! seen [$ 0] ++'이지만 더 빠르지는 않았습니다.
— mgjk

이것은 awkGilles 답변에 확장 설명으로 표시된 2 배열 조회를 사용 하는 더 자세한 버전 보다 놀랍게도 빠릅니다 . 0m36.132s vs 0m49.958s .. 5 천만 줄 .. 병목 현상은 I / O, 그러나 추가 배열 조회는 다음과 같습니다. 배열의 1 백만 요소가 다소 심각한 찌그러짐을 만드는 것 같습니다.
— Peter.O

그러나 그것은 sort -u ....와 어떻게 비교됩니까?
— HashWizard

1
@HashWizard :이 명령은 정렬되지 않지만 다음 줄에서 발생하는 모든 줄을 제거합니다.
— enzotib

1
@ MaxWilliams 예, 무작위로 배포됩니다.
— setholopolus

47

표준 유틸리티를 사용하는 간단한 (명백한 것은 아닙니다) 방법은 run 이외의 대용량 메모리가 필요하지 않습니다 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

@Gilles에 대한 설명에 awk '!seen[$0]++'따르면 awk가 2 개의 중복 행을 보게되면 항상 첫 번째 행을 유지하고 모든 후속 행을 무시한다는 의미입니까? (또는 마지막 것을 유지하겠습니까?)
— user779159

1
@ user779159 첫 번째 행을 유지합니다. 각 입력 행이 즉시 인쇄되거나 (첫 번째 발생) 전혀 인쇄되지 않습니다 (반복 발생).
— Gilles

그러나 그것은 sort -u ...와 어떻게 비교됩니까?
— HashWizard

@HashWizard 일반 sort -u이 순서를 변경합니다. 내 대답은 순서를 유지하는 솔루션을 보여줍니다 (처음 발생하는 순서).
— Gilles

@Gilles는 50 % 중복으로 큰 파일 (10G)의 경우 sort-u보다 빠르다고 말할 수 있습니까?
— HashWizard

25
sort -u big-csv-file.csv > duplicates-removed.csv

출력 파일이 정렬됩니다.


1
awk다른 답변 의 명령 만큼 빠르지는 않지만 개념적으로 간단합니다!
— Johann

@Johann 나는 수십만 (1 백만) 짧은 줄 바꿈 문자열이있는 파일 에서이 작업을 자주 수행합니다. 내가하고있는 실험에 대해 결과가 매우 빠릅니다. 반복해서 실행되는 스크립트에서 사용하는 것이 더 중요 할 수 있으므로 시간을 크게 절약 할 수 있습니다.
— Vladislavs Dovgalecs

1
sort -u정렬하는 동안 이후가 아니라 중복을 제거하는 데 사용 합니다. (메모리 대역폭을 절약) 다른 프로그램으로 파이프). awk출력을 정렬하려는 경우 버전 보다 낫 습니다. (이 질문의 OP는 원래 순서가 유지 되기를 원 하므로 약간 다른 사용 사례에 대한 좋은 대답입니다.)
— Peter Cordes

약 1,500 만 개의 라인 파일 (전체 1.8GB)을 위해 약 1 분이 걸렸습니다. 훌륭한.
— Max Williams

18

중복 제거 된 파일을 메모리에 충분히 저장할 수 있다고 가정하면 (데이터가 실제로 100 배로 복제 된 경우 약 20MiB + 오버 헤드 여야 함) Perl을 사용하면이 작업을 매우 쉽게 수행 할 수 있습니다.

$ perl -ne 'print unless $dup{$_}++;' input_file > output_file

이것은 순서도 유지합니다.

원하는 %dup경우 해시 에서 각 라인의 발생 횟수를 추가 된 무료 보너스로 추출 할 수 있습니다.

원하는 awk경우이 작업도 수행해야합니다 (perl 버전과 동일한 논리, 동일한 순서, dup변수에 수집 된 동일한 데이터 ).

$ awk '{if (++dup[$0] == 1) print $0;}' input_file > output_file

이것은 @Mat이 너무 좋습니다. 파일을 문지르려고했습니다. lol ;-).
— Nikhil Mulley

이제 그의 sed와 awk 매직 위버를 위해 @ManAtWork를 기다리고 있습니다 :-)
— Nikhil Mulley

awk tip :-)을 위해 다시 굉장하다
— Nikhil Mulley

1
중복 된 줄만 제거하기 위해 perl 스크립트를 변경할 수 있습니까?
— dumbledad

2
@dumbledad : uniq그 자체만으로
— Mat

3

적절한 지원을 제공하는 다른 답변이 없으므로 다음은 하나입니다.

gawk -i inplace '!a[$0]++' file

주문이 유지됩니까? 그건 그렇고, 이것은 나를 위해 작동하지 않았습니다. 내 버전은 :GNU Awk 4.0.2
— Leonid

1
@Leonid 네, 그렇습니다. 고유 한 행이 처음으로 인쇄됩니다. 인플레 이스 지원은 2013 년에 릴리스 된 4.1 버전에서 처음 도입되었습니다.
— Jan Chren-rindeal

3

http://www.computerhope.com/unix/uuniq.htm 을 사용할 수 있습니다uniq

uniq 파일에서 반복되는 행을보고하거나 필터링합니다.


답을 할 때 왜 답 이 답 이 되는지에 대해 설명 하는 것이 좋습니다 . 그렇다면이 답변은 이전 답변과 어떻게 다릅니 까?
— Stephen Rauch

1
uniq 매뉴얼 페이지에서 : 참고 : 'uniq' does not detect repeated lines unless they are adjacent. 먼저 정렬하고 비 복제 라인의 순서를 풀어야합니다.
— Vindolin

2

파이썬 원 라이너 :

python -c "import sys; lines = sys.stdin.readlines(); print ''.join(sorted(set(lines)))" < InputFile

이로 인해 전체 파일이 메모리로 슬러 핑되어 OP 문제에 적합하지 않을 수 있습니다. 질서 유지 보장
— iruvar

제안 주셔서 감사합니다, 난 그냥 파이썬을 배우고 .. 그냥 학습 목적으로 이것을 시도 .. :)
— Rahul Patil

여기 한 - 라이너가 아니라 (간결) 인쇄에 공급하기 위해 전체 파일을 메모리로로드하거나 하나의 거대한 문자열을 만드는 중 하나없이 순서를 유지 독특한 라인을 반환하는 파이썬 2.7 버전
— iruvar

내가 뭔가를 1_CR @ 감사합니다 :) 오늘 배울OrderedDict
— 라훌 파틸

0

여기에 대한 답변 중 어느 것도 내 Mac에서 저에게 효과적이지 않았으므로 저에게 맞는 간단한 파이썬 스크립트를 작성했습니다. 선행 / 후행 공백을 무시하고 메모리 소비에 신경 쓰지 않습니다.

import sys

inputfile = sys.argv[1]
outputfile = sys.argv[2]

with open(inputfile) as f:
    content = f.readlines()

content = [x.strip() for x in content]

my_list = list(set(content))

with open(outputfile, 'w') as output:
    for item in my_list:
        output.write("%s\n" % item)

위의 unique.py에 저장하고 다음과 같이 실행하십시오.

python unique.py inputfile.txt outputfile.txt

-1

bash 4를 사용하면 연관 배열을 이용하는 순수 bash 솔루션을 사용할 수 있습니다. 여기에 예가 있습니다

unset llist; declare -A llist;
while read -r line; do
if [[ ${llist[$line]} ]]; then
  continue
else 
  printf '%s\n' "$line"
  llist[$line]="x"
fi
done < file.txt

2
read큰 텍스트 파일을 처리 하기 위해 루프를 사용하지 마십시오 . bash는 줄 바꿈을 피하기 위해 한 번에 한 바이트 씩 읽어야합니다. 배쉬는 일반적으로 awk에 비해 텍스트 처리 속도가 빠르지 않습니다. 이것을 사용하면 read -ra입력에서 백 슬래시를 피할 수 있습니다. 또한 이것을 쉘 함수에 넣거나 대화식으로 사용하는 경우 루프 unset llist 뒤를 잊지 마십시오 .
— Peter Cordes

2
@PeterCordes, 또는 당신은 이것을 참조 할 수 있습니다 :-)
— iruvar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.