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


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.