문자열에서 인접한 중복 단어 제거

문자열에서 인접한 중복 단어 제거

다음과 같은 문자열이 있습니다.

one one tow tow three three tow one three

중복된 단어를 제거하고 다음과 같이 만들려면 어떻게 해야 합니까?

one tow three tow one three

요점은 인접한 중복 단어만 제거하는 스크립트를 작성하고 싶다는 것입니다

나는 시도했다:

echo "$string" | awk '{for (i=1;i<=NF;i++) if (!a[$i]++) printf("%s%s",$i,FS)}{printf("\n")}'

그러나 인접하지 않은 중복 단어도 제거합니다.

답변1

다중 문자 RS\s속기용으로 GNU awk 사용:

$ echo 'one one tow tow three three tow one three' |
awk -v RS='\\s+' '
    $0 != prev { out = (NR>1 ? out OFS : "") $0; prev = $0 }
    END { print out }
'
one tow three tow one three

또는 여전히 GNU awk이지만 다음에서 영감을 받았습니다.@nezabudka의 답변그러나 입력 필드를 구분하는 공백 순서와 입력 필드에 포함된 문자에 관계없이 올바르게 작동하고 출력이 이와 같이 끝나도록 일부 수정이 이루어졌으므로 \n유효한 POSIX 텍스트입니다. 문서:

$ echo one one tow tow three three tow one three |
awk -v RS='[[:blank:]]+' '
    $1 != prev { out = out $1 RT; prev=$1 }
    END { print out }
'
one tow three tow one three

그렇지 않으면 awk를 사용하십시오.

$ echo 'one one tow tow three three tow one three' |
awk '{
    out = $1
    for ( i=2; i<=NF; i++ ) {
        if ( $i != $(i-1) ) {
            out = out OFS $i
        }
    }
    print out
}'
one tow three tow one three

답변2

행에 2500개(예: 1000개) 이하의 열이 있는 경우:

echo one one tow tow three three tow one three |
    fmt -1 | uniq | fmt -1000

GNU awk:

echo one one tow tow three three tow one three |
    awk -v RS=' ' '$1 != D {printf "%s", $1 (RT?RS:ORS); D=$1}'

업데이트(줄 바꿈으로 끝나는 것이 확실한 경우):

echo one one tow tow three three tow one three |
    awk -v RS='[[:space:]]' '$1 != D {printf "%s", $1 RT; D=$1}'

그렇지 않은 경우(일반적인 방법):

echo -n one one tow tow three three tow one three |
    awk -v RS='[[:space:]]' '$1 != D {printf "%s", $1 (RT?RT:ORS); D=$1}'

참고:
GNU 버전에는 RS의 템플릿에 해당하는 실제 값이 할당되는 내장 변수 RT가 있습니다. 예를 들어, [[:space:]]RS 변수에 대해 템플릿이 정의된 경우 RT 변수에는 각 경우(공백, 탭 또는 개행)에서 레코드를 종료하는 문자가 동적으로 할당됩니다. RS 변수에 문자 클래스 템플릿이 할당된 경우 RS=[[:space:]]삼항 연산자를 (RT?RT:ORS)또는 로 변경해야 합니다.RT

답변3

uniq다른 줄 뒤에 모든 단어를 넣으려면 다음을 사용할 수 있습니다.

string='one one tow tow three three tow one three'
printf '%s\n' "${string// /
}" | uniq | paste -sd ' ' -

또는 를 사용하여 perl여러 공백 문자를 허용하여 단어를 구분하고 반복 그룹 사이의 공백을 유지합니다.

string='  one one tow   tow  three three tow one three '
perl -le 'print s/(?<!\S)(\S+)(\s+\1)+(?!\S)/\1/gr for @ARGV' -- "$string"

다음을 제공합니다:

  one tow  three tow one three 

ksh93의 ${var//pattern/replacement}매개변수 확장 연산자와 동일합니다(bash를 포함한 일부 다른 쉘은 이 연산자를 복사했지만 고급 패턴 일치 연산자는 복사하지 않았습니다).

$ string='  one one tow   tow  three three tow one three '
$ print -r - "${string//~(<!\S)+(\S)+(+(\s)\1)~(!\S)/\1}"
  one tow  three tow one three

또는 zsh(다른 쉘은 Perl과 유사한 패턴 일치 연산자를 지원함)을 사용하여 해당 변수를 수정합니다.

$ string='  one one tow   tow  three three tow one three '
$ autoload regexp-replace
$ set -o rematchpcre
$ regexp-replace string '(?<!\S)(\S+)(\s+\1)+(?!\S)' '$match[1]'
$ print -r - "$string"
  one tow  three tow one three 

또는 fish:

$ set string '  one one tow   tow  three three tow one three '
$ string replace -a --regex '(?<!\S)(\S+)(\s+\1)+(?!\S)' '$1' $string
  one tow  three tow one three 

예제의 단어가 모두 숫자(또는 밑줄)로 구성된 경우 비지박스 구현과 유사한 접근 방식을 취할 수 있습니다. awk여기서 음수 탐색 perl 연산자는 \<\>단어 경계 연산자로 대체될 수 있습니다(perl의 와 유사 하므로 / \b에 더 가깝습니다 ). Perl 둘러보기 연산자로):(?<!\w)(?!\w)

$ printf '%s\n' "$string" | busybox awk '{print gensub("\\<(\\S+)(\\s+\\1)+\\>", "\\1", "g")}'
  one tow  three tow one three

단어에 숫자나 밑줄 이외의 문자가 포함되어 있으면 이 방법을 사용할 수 없습니다. 예를 들어 와 와 사이에 단어 경계가 있기 one-two two three때문에 로 변경됩니다.one-two three-two

답변4

펄을 사용하세요. 예를 들어, 다음은 줄 경계를 넘어 인접한 중복 단어를 제거할 수도 있습니다(perl의 -0777옵션을 사용하여 전체 입력을 한 번에 흡수함).

$ printf 'one one two\n two two\ntwo three three two\none\nthree\nthree\n' |
    perl -0777 -p -e 's/\b(\w+)(?:\s+\1)+\b/$1/g'
one two three two
one
three

\1작업의 왼쪽(LHS)은 s/search (LHS)/replace (RHS)/이전에 일치된 패턴 그룹에 대한 역참조 입니다 (\w+). $1교체 작업 또는 작업 오른쪽에 있는 동일한 캡처 그룹입니다.

그런데 이것을 Perl에 입력하지 않으면 입력은 다음과 같이 보입니다. 여러 줄에는 반복되는 인접 단어가 포함되어 있습니다.

$ printf 'one one two\n two two\ntwo three three two\none\nthree\nthree\n' 
one one two
 two two
two three three two
one
three
three

노트:

  1. \b^or 와 유사한 앵커이지만 $줄의 시작이나 끝을 일치시키는 대신 단어 사이의 (너비가 0인) 경계와 일치합니다.

  2. \w매뉴얼 페이지에 다음과 같이 정의된 모든 단어 문자와 일치합니다 perlre.

\w [3]은 "단어" 문자와 일치합니다(영숫자와 "_", 기타 연결 구두점 및 유니코드 마커).

...

[3] 자세한 내용은 perlunicode의 "유니코드 문자 속성"을 참조하세요.

알파벳(예: 알파벳) 문자(숫자나 밑줄 제외)만 엄격하게 일치시키려는 경우 [[:alpha:]]+대신 를 사용할 수 있습니다 \w+.

  1. 입력 텍스트에 유니코드 문자가 포함된 경우 이를 처리하는 방법에는 여러 가지가 있지만 가장 간단한 방법은 Perl의 -C옵션을 사용하는 것입니다.
$ echo 'öne öne öne two öne one' |
    perl -C -0777 -p -e 's/\b([[:alpha:]]+)(?:\s+\1)+\b/$1/g'
öne two öne one

이 옵션에 대한 세부정보를 보고 man perlrun검색하세요 . -C이 주제에 정말로 관심이 있다면 , perlunicodeperlunitutperluniintro대한 매뉴얼 페이지도 참조하십시오 perlunifaq. 광범위한 문서에서 짐작할 수 있듯이 유니코드를 다루는 것은 대부분의 경우 간단하고 간단하지만 다양한 상황에서는 매우 복잡하고 미묘할 수 있습니다.

관련 정보