텍스트 파일에서 2개 이상의 단어(공백으로 구분되지 않음)가 포함된 줄을 제거하는 방법은 무엇입니까? [폐쇄]

텍스트 파일에서 2개 이상의 단어(공백으로 구분되지 않음)가 포함된 줄을 제거하는 방법은 무엇입니까? [폐쇄]

텍스트 파일에서 2개 이상의 단어(공백으로 구분되지 않음)가 포함된 줄을 제거하는 방법은 무엇입니까?

문서에는 이러한 단어의 "단일 버전"도 있습니다.

예를 들어:

alpha
beta
gama
alphabeta
zeta
gamabeta

출력은 다음과 같아야 합니다.

alpha
beta
gama
zeta

편집하다: 내 파일에는 150만 줄이 포함되어 있습니다.

답변1

상당히 짧은 파일의 경우 행에 ERE 연산자가 포함되어 있지 않다고 가정하면 다음과 같습니다.

$ LC_ALL=C grep -vxE "($(paste -sd '|' file)){2,}" file
alpha
beta
gama
zeta

2개 이상의 행 시퀀스를 포함하지 않는 행을 반환합니다 file.

작동 방식은 grep다음과 같은 명령을 작성하는 것입니다.

LC_ALL=C grep -vxE '(alpha|beta|gama|alphabeta|zeta|gamabeta){2,}' file

더 큰 파일의 경우 길이 또는 매개변수 + 환경(또는 Linux의 단일 매개변수) 제한에 직면하게 됩니다. 인수 대신 표준 입력을 사용하여 정규식을 전달하면 이 문제를 해결할 수 있지만 -f -, 그래도 정규식 크기에 한계가 있습니다.

perl대신 다음을 사용하여 grep더 큰 입력을 처리할 수 있습니다 .

perl -le '
  chomp (@words = <>);
  $re = "^(" . join("|", map {qr{\Q$_\E}} @words) . "){2,}\\z";
  for (@words) {print unless m/$re/}' file

(이것은 위에서 언급한 다른 제한 사항도 방지합니다).

각 단어를 다른 단어와 비교해야 하기 때문에(아마도 두 번 이상) 어쨌든 시간이 오래 걸립니다.

답변2

그러면 파일에 있는 두 단어의 조합이 아닌 파일의 모든 단어가 인쇄됩니다.

$ awk '{one[NR]=$1} END{for (i=1;i<=length(one);i++) for (j=1;j<=length(one);j++) two[one[i] one[j]]; for (i=1;i<=length(one);i++) if (!(one[i] in two)) print one[i]}' file
alpha
beta
gama
zeta

명령을 여러 줄로 나누고 싶은 분들을 위해:

awk '
    {
    one[NR]=$1
    }

    END{
        for (i=1;i<=length(one);i++)
            for (j=1;j<=length(one);j++)
                two[one[i] one[j]]
        for (i=1;i<=length(one);i++)
            if (!(one[i] in two))
                print one[i]
     }' file

다른 예시

유사한 단어가 포함된 파일을 고려해 보겠습니다. 하지만 때로는 개별 단어 앞에 조합이 나타나는 경우가 있습니다.

$ cat file2
alphabeta
alpha
gammaalpha
beta
gamma

동일한 명령을 실행해도 여전히 올바른 결과가 생성됩니다.

$ awk '{one[NR]=$1} END{for (i=1;i<=length(one);i++) for (j=1;j<=length(one);j++) two[one[i] one[j]]; for (i=1;i<=length(one);i++) if (!(one[i] in two)) print one[i]}' file2
alpha
beta
gamma

어떻게 작동하나요?

  • one[NR]=$1

    one그러면 키가 줄 번호 NR이고 값이 해당 줄의 단어인 배열이 생성됩니다 .

  • END{...}

    중괄호 안의 명령은 파일 읽기가 완료된 후에 실행됩니다. 이 명령은 두 개의 루프로 구성됩니다. 첫 번째 루프는 다음과 같습니다.

     for (i=1;i<=length(one);i++)
          for (j=1;j<=length(one);j++)
              two[one[i] one[j]]
    

    two그러면 파일에 있는 두 단어의 모든 조합으로 구성된 키가 포함된 배열이 생성됩니다 .

    두 번째 루프는 다음과 같습니다.

      for (i=1;i<=length(one);i++)
          if (!(one[i] in two))
              print one[i]
    

    이 루프는 배열의 키로 나타나지 않는 파일의 모든 단어를 인쇄합니다 two.

더 짧고 간단한 버전

이 버전은 더 짧은 코드를 사용하고 동일한 단어를 인쇄합니다. 단점은 단어의 순서가 입력 파일과 동일하다고 보장되지 않는다는 것입니다.

$ awk '{one[$1]} END{for (w1 in one) for (w2 in one) two[w1 w2]; for (w in one) if (!(w in two)) print w}' file1
gama
zeta
alpha
beta

메모리를 더 절약하는 방법

대용량 파일의 경우 위의 방법으로 인해 메모리 오버플로가 발생할 수 있습니다. 이러한 경우 다음을 고려하십시오.

$ sort -u file | awk '{one[$1]} END{for (w1 in one) for (w2 in one) print w1 w2}' >doubles
$ grep -vxFf doubles file
alpha
beta
gama
zeta

이는 sort -ufile1에서 중복된 단어를 제거한 다음 이라는 이중 단어를 포함할 수 있는 파일을 만드는 데 사용 됩니다 doubles. 그런 다음 .grepfiledoubles

답변3

<file awk 'NF {print length "\t" $0}' | sort -k1n,1 | cut -f2- |
awk 'NR==1 {min=length}
(l=length) >= 2*min {
  delete k; # clear k array
  k[1];
  while (length(k))
    for (i in k) {
      for (j=l-i+1; j>=min; --j)
        if (substr($0,i,j) in seen) {
          if (i+j-1==l)
            next;
          k[i+j];
        }
      delete k[i];
    }
}
!seen[$0]++'

이전에 본 줄로만 구성된 줄은 인쇄되지 않습니다.

이미 표시된 문자열에 하위 문자열이 있는지 확인하여 작동합니다.

입력 파일을 줄 길이에 따라 가장 짧은 것부터 가장 긴 것까지 정렬해야 합니다. awk | sort | cut이 방법.

다음 awk프로그램은 먼저 가장 짧은 줄의 길이를 기록합니다( 로 저장됨 min). 길이가 다음보다 작은 줄은 해당 2*min하위 문자열을 확인할 필요가 없습니다. 대신 seen배열 해시 에 추가하고 인쇄할 수 있습니다 ( !seen[$0]++중복되지 않은 항목을 인쇄하기 위한 조건으로 사용됨, 추가 정보:awk '!a[$0]++'는 어떻게 작동하나요?). min하위 문자열을 확인할 때 컷오프 길이로 사용할 수도 있습니다.

부분 문자열에 대한 라인을 스캔할 때 가능한 새로운 시작 위치를 모두 기록해야 합니다. 이는 k이러한 오프셋을 저장하는 배열을 사용하여 수행됩니다. 하위 문자열을 검색하고 해당 문자열이 배열의 해시로 존재하는지 확인하세요 seen. 표시된 문자열이 발견되면:

  • 하위 문자열이 줄 끝에 있으면 next입력 줄로 이동합니다. 해당 행은 인쇄되지 않거나 표시 배열에 추가되지 않습니다.
  • 그렇지 않으면 다음 시작 위치를 추가 k하고 더 많은 하위 문자열을 계속 검색합니다.
  • 새로운 시작 위치를 찾는 동안 계속 시도하십시오 ( while (length(k))).
  • 위의 루프가 다음 줄로 진행되지 않으면 해당 줄이 seen배열 해시에 추가됩니다(또는 아직 표시되지 않은 경우 인쇄됩니다).

답변4

awk '{for (i in a) if (index($0,i)) next; print; a[$0]}' file

관련 정보