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

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

필요한 것은 원래 시퀀스 순서를 유지하면서 모든 반복을 제거하는 것입니다 (바람직하지만 성능 향상을 위해 희생 할 수 있음). 결과적으로 각 줄은 고유해야합니다. 만약 100 개의 동일한 라인이 있다면 (보통 중복은 파일 전체에 퍼져 있고 이웃이 아닐 것입니다) 그 종류 중 하나만 남게됩니다.

나는 Scala로 프로그램을 작성했습니다. Scala에 대해 모르는 경우 Java)를 구현합니다. 하지만이 작업을 더 빨리 수행 할 수있는 더 빠른 C 작성 기본 도구가 있습니까?

업데이트 : awk "!seen[$0]++" filename 솔루션은 파일이있는 한 저에게 잘 작동하는 것 같았습니다. 2GiB 이하에 가깝지만 8GiB 파일을 정리할 때 더 이상 작동하지 않습니다. 4GiB RAM이있는 Mac과 4GiB RAM이있는 64 비트 Windows 7 PC에서 무한대를 차지하는 것 같습니다. 그리고 6GiB 스왑은 메모리가 부족합니다. 그리고이 경험을 감안할 때 4GiB RAM이있는 Linux에서 시도하는 데 열광하지 않습니다.

댓글

  • 이렇게하면 주문이 파괴되지만 sort -u를 시도해 본 적이 있습니까? 이렇게 방대한 파일에서 어떻게 실행될 수 있는지 모르겠습니다.
  • C는 종종 Java보다 훨씬 빠르지 않습니다. 당신은 ‘ 지금 (순서대로) 실행하고 있습니다. ‘ 공정한 기회입니다. ‘ 여기에서 답을 얻고 구현하고 실행을 완료하기 전에 완료됩니다. 순서가 맞지 않으면 sort -u가 더 빠를 것입니다.

답변

#bash (Freenode)에 표시된 awk 솔루션 :

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

댓글

  • 2G 파일에서이 작업을 시도했고 내 노트북에서 3 분이 걸렸습니다. 나쁘지 않다. 나는 또한 유일한 파일 이름을 시도했다 | awk ‘! seen [$ 0] ++ ‘, 그러나 ‘ 아무도 더 빠릅니다.
  • @HashWizard :이 명령은 정렬되지 않지만 다음 번에 같은 줄이 나타날 때마다 제거합니다.
  • 이 명령이 어떻게 작동하는지 궁금하십니까? -여기 참조 : unix.stackexchange.com/questions/159695/how-does-awk-a0-work
  • @MaxWilliams yes , 효과는 무작위로 배포됩니다.
  • 줄 바꿈 또는 공백이있는 줄 유지 awk '/^\s*?$/||!seen[$0]++'

답변

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 

모든 줄이 a로 시작하는 경우 공백이 아닌 문자의 경우 다음 옵션 중 일부를 생략 할 수 있습니다.

<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 첫 번째 항목을 유지합니다. 각 입력 행은 즉시 인쇄되거나 (첫 번째 발생) 전혀 인쇄되지 않습니다 (반복 발생).
  • @ user779159 li>
  • 하지만 sort -u …와 비교하면 어떻습니까?
  • @HashWizard 일반 sort -u는 순서를 변경합니다.내 대답은 순서 (정확하게는 첫 번째 발생 순서)를 보존하는 솔루션을 보여줍니다.
  • @Gilles 50 % 중복이있는 대용량 파일 (10G)의 경우 sort -u보다 빠르다고 말씀하겠습니까? ?

Answer

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

출력 파일은 정렬됩니다.

댓글

  • 다른 답변의 awk 명령만큼 빠르지는 않지만 개념적으로 간단합니다!
  • @Johann 저는 수십만 (심지어 백만) 개의 짧은 줄 바꿈으로 끝나는 문자열이있는 파일에서이 작업을 자주 수행합니다. 내가하는 실험에 대한 결과를 꽤 빨리 얻습니다. 반복 실행되는 스크립트에서 사용하면 더 중요 할 수 있으며 시간을 상당히 절약 할 수 있습니다.
  • 정렬 중에 중복을 제거하려면 sort -u를 사용합니다. 이후보다는. (그리고 메모리 대역폭을 절약합니다) 다른 프로그램으로 파이핑). 출력도 정렬하려는 경우 awk 버전보다 낫습니다. (이 질문에 대한 OP는 원래 주문이 보존 되기를 원하므로 약간 다른 사용 사례에 대한 좋은 답변입니다.)
  • 잠깐 시간을내어 550 만 라인 파일 (총 1.8GB). 훌륭합니다.

답변

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

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

이 주문도 유지합니다.

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

p>

awk를 선호하는 경우이 작업도 수행해야합니다 (펄 버전과 동일한 논리, 동일한 순서, 동일한 데이터가 dup 변수) :

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

댓글

  • 이거 너무 좋아요 @Mat, I ㅋㅋ;-).
  • 이제 그의 sed와 awk magic weavery를 위해 @ManAtWork를 기다리고 있습니다 🙂
  • awk 팁을 위해 다시 굉장합니다 :- )
  • perl 스크립트를 remov로만 변경할 수 있습니까? 인접한 줄이 중복됩니까?
  • @dumbledad : uniq이 모든 작업을 자체적으로 수행합니다.

답변

인플레 이스 지원을 제공하는 다른 답변은 없지만 다음은 하나입니다.

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

댓글

  • 순서가 유지됩니까? 그건 그렇고, 이것은 나를 위해 작동하지 않았습니다. 내 버전은 다음과 같습니다. GNU Awk 4.0.2
  • @Leonid 예, 그렇습니다. 고유 한 행의 첫 번째 발생을 인쇄합니다. 인플레 이스 지원은 2013 년에 출시 된 버전 4.1에서 처음 도입되었습니다.
  • 이게 답입니다. ‘ 실제로 기존 또는 현재 파일에서 중복 된 문자열을 삭제합니다. 여기에서 최상위 답변과 대부분의 답변은 고유 / 중복 된 문자열 만 출력하고 아무것도하지 않고 생성해야합니다. 결과를 저장하기위한 다른 출력입니다.

Answer

uniq http://www.computerhope.com/unix/uuniq.htm

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

댓글

  • 답변을 제공 할 때 당신의 대답에 대한 몇 가지 설명 이 그 중 하나입니다. 그렇다면이 답변은 이전 답변과 어떻게 다른가요?
  • Uniq man 페이지에서 : 참고 : 'uniq' does not detect repeated lines unless they are adjacent. 따라서 먼저 정렬하고 느슨하게해야합니다. 중복되지 않는 줄의 순서입니다.

답변

Python One 라이너 :

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

댓글

  • 이로 인해 전체 파일이 메모리에 저장되며 OP ‘ 문제에 적합하지 않을 수 있습니다. 또한 순서를 유지하는 것이 보장되지 않습니다.
  • 제안 해 주셔서 감사합니다. ‘ 방금 파이썬을 배우고 있습니다 .. 학습 목적으로이 작업을 시도했습니다 .. 🙂
  • 여기 ‘ s 는 한 줄짜리가 아니지만 (간결하게) Python 2.7 버전입니다. 전체 파일을 메모리에로드하거나 인쇄 할 하나의 거대한 문자열을 생성하지 않고 순서를 유지하는 고유 한 줄을 반환합니다.
  • @ 1_CR 감사합니다. 오늘 배운 내용이 있습니다. 🙂 OrderedDict

Answer

여기에있는 답변 중 어느 것도 Mac에서 저에게 효과가 없었기 때문에 간단한 Python을 작성했습니다. 나를 위해 작동하는 스크립트. 선행 / 후행 공백을 무시하고 메모리 소비도 신경 쓰지 않습니다.

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) 

위의 내용을 고유하게 저장합니다.py 및 다음과 같이 실행하십시오.

python unique.py inputfile.txt outputfile.txt 

답변

원래 시퀀스 순서를 유지하지 않은 솔루션

다음 코드 조각으로 작업했습니다.

sort duplicates.txt | uniq > noDuplicates.txt 

sort 명령은 행을 알파벳순으로 정렬하고 uniq 명령은 중복을 제거합니다.

참고 : 라인을 먼저 정렬 한 이유는 는 인접하지 않는 한 중복 된 줄을 감지하지 않습니다.

설명

  • 질문은 방법을 요청합니다 (가급적이면 ) 입력 순서를 유지합니다. 이 문제를 해결하기 위해 답변을 수정 할 수 있습니까? 입력 순서를 유지하는 sort를 사용하는 기존 답변과 iv id = “70fbddf299를 사용하는 하나의 답변 이 있습니다. “>

입력 순서를 유지하지 않고uniq에 배관하는 것보다 더 효율적인 방식입니다.

  • @StephenKitt 수정 됨. 다른 답변을 조사했지만 ‘ 기본 명령만으로는 아무것도 찾을 수 없었습니다. 의견을 보내 주셔서 감사합니다.
  • 기본 명령 만 포함 된 답변에 대한 링크 를 제공했습니다. 실제로는 하나의 명령 인 sort -u ( POSIX 의 일부 임);-).
  • @StephenKitt 그 답을 보았습니다. 광산은 또한 문제를 처리하는 방법입니다. 내가 더 많은 일을하기를 원하십니까? 답변을 삭제해야합니까?
  • 아니요, 답변을 삭제하지 마십시오. “기본 명령만으로는 아무것도 찾을 수 없었습니다. ‘
    기본 명령만으로는 아무것도 찾을 수 없습니다.”라고했기 때문에 다른 답변을 알고 있는지 확인하고 싶었습니다.

    답변

    연관 배열을 활용하는 순수 Bash 솔루션 인 bash 4 를 사용할 수 있습니다. 다음은 예입니다.

    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 

    댓글

    • 돈 ‘ 큰 텍스트 파일을 처리하기 위해 read 루프를 사용하지 마십시오. bash는 줄 바꿈을 초과하지 않도록 한 번에 한 바이트 씩 읽어야합니다. Bash는 또한 일반적으로 awk에 비해 텍스트 처리 속도가 빠르지 않습니다. 이것을 사용하면 read -ra 입력에 백 슬래시를 사용하지 않습니다. 또한 ‘이를 셸 함수에 넣거나 루프를 실행하는 경우 루프 뒤에 unset llist 뒤에 하는 것을 잊지 마십시오. 대화식으로 사용하십시오.
    • @PeterCordes 또는 방금 this 를 참조했을 수 있습니다. 🙂
  • 답글 남기기

    이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다