모든 n-gram을 찾는 하나의 쉘 명령이 텍스트에서 작동합니다.

모든 n-gram을 찾는 하나의 쉘 명령이 텍스트에서 작동합니다.

공백으로 구분된 단어가 포함된 텍스트 스트림이나 파일이 있습니다. 좋다:

I have a toy. you may not like it.

공백으로 구분된 모든 단어는 두 개 이상의 작은 단어로 구성될 수 있으며, 다음과 같이 낙타 표기법(다른 대소문자로 구분), 뱀 표기법(밑줄로 구분) 또는 점으로 구분하여 구성할 수 있습니다.

I_amAManTest you_haveAHouse FOO_BAR_test.model

예를 들어:

I_amAManTest

다음과 같이 나눌 수 있습니다:

I
am
A
Man
Test

하지만 난 매일 인쇄하고 싶어N다음과 같은 복합어의 단어(인접한 작은 단어의 모든 하위 집합)

I_amAManTest

산출:

// from first word on
I
I_am
I_amA
I_amAMan
I_amAManTest
// from second word on 
am
amA
amAMan
amAManTest
// from third word on 
A
AMan
AManTest
// from fourth word on
Man
ManTest
// from fifth word on
Test

결론적으로 다음과 같은 입력의 경우

I_amAManTest you_haveAHouse FOO_BAR_test

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

I
I_am
I_amA
I_amAMan
I_amAManTest
am
amA
amAMan
amAManTest
A
AMan
AManTest
Man
ManTest
Test
you
you_have
you_haveA
you_haveAHouse
have
haveA
haveAHouse
A
AHouse
House
FOO
FOO_BAR
FOO_BAR_test
BAR
BAR_test
test

답변1

(대부분-) sed솔루션:

cat "$@" |
    tr -cs -- '._[:alpha:]' '[\n*]' |
    sed -n  -e 'h; :ms' \
            -e 'p; :ss' \
                -e 's/\([[:lower:]]\)[[:upper:]][[:lower:]]*$/\1/p; t ss' \
                -e 's/\([[:lower:]]\)[[:upper:]][[:upper:]]*$/\1/p; t ss' \
                -e 's/\([[:upper:]]\)[[:upper:]][[:lower:]]\+$/\1/p; t ss' \
                -e 's/[._][[:alpha:]][[:lower:]]*$//p; t ss' \
                -e 's/[._][[:upper:]]\+$//p; t ss' \
            -e 'g' \
            -e 's/^[[:upper:]]\?[[:lower:]]\+\([[:upper:]]\)/\1/; t mw' \
            -e 's/^[[:upper:]]\+\([[:upper:]][[:lower:]]\)/\1/; t mw' \
            -e 's/^[[:alpha:]][[:lower:]]*[._]//; t mw' \
            -e 's/^[[:upper:]]\+[._]//; t mw' \
            -e 'b' \
            -e ':mw; h; b ms'

알고리즘은

for each compound word (e.g., “FOO_BAR_test”) in the input
do
    repeat
        print what you’ve got
        repeat
            remove a small word from the end (e.g., “FOO_BAR_test” → “FOO_BAR”) and print what’s left
        until you’re down to the last one (e.g., “FOO_BAR_test” → “FOO”)
        go back to what you had at the beginning of the above loop
          and remove a small word from the beginning
          (e.g., “FOO_BAR_test” → “BAR_test”) ... but don’t print anything
    until you’re down to the last one (e.g., “FOO_BAR_test” → “test”)
end for loop

세부:

  • cat "$@"UUOC입니다. 나는 보통 이것을 피합니다. 할 수는 있지만 여러 파일을 직접 전달할 수는 없습니다  .tr args <filetr
  • tr -cs -- '._[:alpha:]' '[\n*]'많은 복합어의 줄을 별도의 줄로 나눕니다. 예:
    I_amAManTest you_haveAHouse FOO_BAR_test
    
    된다
    I_amAManTest
    you_haveAHouse
    FOO_BAR_test
    
    그래서 sed는 한 번에 하나의 복합어를 처리할 수 있습니다.
  • sed -n— 아무것도 자동으로 인쇄하지 마세요. 명령을 받은 경우에만 인쇄합니다.
  • -e다음을 지정합니다.이자형xpression은 sed 스크립트의 일부입니다.
  • h— 패턴 공간을 홀드 공간에 복사합니다.
  • :ms— 라벨(메인 루프 시작)
  • p— 인쇄
  • :ss— 라벨(보조 루프 시작)
  • 다음 명령은 복합어의 끝에서 작은 단어를 제거하고, 성공하면 결과를 인쇄하고 보조 루프의 시작 부분으로 다시 이동합니다.
    • s/\([[:lower:]]\)[[:upper:]][[:lower:]]*$/\1/p; t ss— "nTest"를 "n"으로 변경합니다.
    • s/\([[:lower:]]\)[[:upper:]][[:upper:]]*$/\1/p; t ss— "mOK"를 "m"으로 변경합니다.
    • s/\([[:upper:]]\)[[:upper:]][[:lower:]]\+$/\1/p; t ss— "AMan"을 "A"로 변경합니다.
    • s/[._][[:alpha:]][[:lower:]]*$//p; t ss— "_am"을 삭제합니다(아무 것도 대체하지 않음).
    • s/[._][[:upper:]]\+$//p; t ss— "_BAR"을 삭제합니다(아무 것도 대체하지 않음).
  • 이것이 보조 루프의 끝입니다.
  • g— 홀드 공간을 패턴 공간에 복사합니다(위 루프의 시작 부분으로 돌아갑니다).
  • 다음 명령은 복합어의 시작 부분에서 작은 단어를 제거하고, 성공하면 메인 루프(mw = 메인 루프 요약)의 ​​끝으로 점프합니다.
  • s/^[[:upper:]]\?[[:lower:]]\+\([[:upper:]]\)/\1/; t mw— "amA"를 "A"로, "ManT"를 "T"로 변경합니다.
  • s/^[[:upper:]]\+\([[:upper:]][[:lower:]]\)/\1/; t mw— "AMa"를 "Ma"로 변경합니다.
  • s/^[[:alpha:]][[:lower:]]*[._]//; t mw— "I_" 및 "you_"를 삭제합니다(아무 것도 대체하지 않음).
  • s/^[[:upper:]]\+[._]//; t mw— "FOO_"를 삭제합니다(아무 것도 대체하지 않음).
  • 위의 각 대체 명령은 성공하면(뭔가를 찾거나 일치하는 경우) 메인 루프 요약(아래)으로 점프합니다. 여기까지 오면 패턴 공간에는 작은 단어만 포함되어 있으므로 작업은 끝난 것입니다.
  • b— sed 스크립트 끝으로 분기(점프)합니다. 즉, sed 스크립트를 종료합니다.
  • :mw— 메인 루프 마무리 라벨.
  • h— 메인 루프의 다음 반복을 위해 설정하기 위해 패턴 공간을 보류 공간에 복사합니다.
  • b ms— 메인 루프의 시작 부분으로 점프합니다.

요청된 출력을 생성합니다. 불행히도 다른 순서로 배치됩니다. 그게 중요하다면 아마 고칠 수 있을 것 같아요.

$ echo "I_amAManTest you_haveAHouse FOO_BAR_test" | ./myscript
I_amAManTest
I_amAMan
I_amA
I_am
I
amAManTest
amAMan
amA
am
AManTest
AMan
A
ManTest
Man
Test
you_haveAHouse
you_haveA
you_have
you
haveAHouse
haveA
have
AHouse
A
House
FOO_BAR_test
FOO_BAR
FOO
BAR_test
BAR
Test

답변2

가장 좋은 방법은 Perl용 토크나이저 모듈을 찾는 것입니다. Grep은 -PPCRE가 필요할 수 있는 여러 실행 없이는 이 작업을 수행할 수 없습니다 .

Perl 모듈이 없는 부분 솔루션은 다음과 같습니다.

while (<>) {
  my $n = 1;
  while (/(\S+)/g) {
    printf "// outputting whitespace-separated word %d\n", $n++;
    my $whole = $1;
    while ($whole =~ /([a-zA-Z0-9][a-z]*+)/g) {
      print "$1\n";
    }
    print "$whole\n";    # whole space-delimited tokens
  }
}

이는 표준 입력 또는 파일에서 한 번에 한 줄씩 입력을 읽습니다. $n인쇄된 주석에 대한 단어 카운터입니다. 그런 다음 단어를 통해 반복합니다(공백으로 설명됨에 따라 정규식은 /(\S+)/g연속된 비공백 문자와 전역적으로 일치함). 각 단어 내에서 다음을 사용하여 토큰 부분을 반복합니다.([a-zA-Z0-9][a-z]*+), 일치 항목은 모두 숫자나 문자로 시작하고 그 뒤에 0개 이상의 소문자가 옵니다( 역추적을 비활성화 *+하여*리도스). 단어에서 일치하는 토큰을 모두 인쇄한 후 전체 단어를 인쇄합니다.

다음과 같이 인라인으로 실행하거나 다음과 같이 실행합니다 perl solution.pl intput.txt.

$ echo "I_amAManTest you_haveAHouse FOO_BAR_test.model" |perl solution.pl
// outputting whitespace-separated word 1
I
am
A
Man
Test
I_amAManTest
// outputting whitespace-separated word 2
you
have
A
House
you_haveAHouse
// outputting whitespace-separated word 3
F
O
O
B
A
R
test
model
FOO_BAR_test.model

여기에는 단어의 다중 부분 하위 토큰이 누락되어 있습니다.

또한 , , 로 I_AmAMan구문 분석하라는 요청은 위 코드처럼 , , , ... 대신 , 로 구문 분석하라는 요청과 충돌합니다 . (아마도 더 나은 예는 다음과 같습니다. 무엇이 되어야 할까요? 3개의 유니그램이 될까요, 아니면 4개의 유니그램이 될까요?)IAmAManFOO_BARFOOBARFOOBI_AmOK

답변3

시작은 다음과 같습니다. 대문자와 소문자의 혼합이 포함된 문자열에 대한 요구 사항을 파악하고 질문에 표시되는 순서대로 출력을 인쇄하려면 이를 처리하면 됩니다.

$ cat tst.awk
{
    for (wordNr=1; wordNr<=NF; wordNr++) {
        delete ngrams
        word = $wordNr
        ngrams[word]
        print "word", word
        numUndSeps = split(word,undSeps,/_/)
        for (undSepNr=1; undSepNr<=numUndSeps; undSepNr++) {
            undSep = undSeps[undSepNr]
            ngrams[undSep]
            print "undSep", undSep
            numDotSeps = split(undSep,dotSeps,/[.]/)
            for (dotSepNr=1; dotSepNr<=numDotSeps; dotSepNr++) {
                dotSep = dotSeps[dotSepNr]
                ngrams[dotSep]
                print "dotSep", dotSep
                while ( match(dotSep,/[[:upper:]]+[^[:upper:]]+/) ) {
                    camel = substr(dotSep,RSTART,RLENGTH)
                    dotSep = substr(dotSep,RSTART+RLENGTH)
                    ngrams[camel]
                    print "camel", camel
                }
            }
        }
        print "-----------"
        for (ngram in ngrams) {
            print ngram
        }
        print "###########"
    }
}

.

$ awk -f tst.awk file
word I_amAManTest
undSep I
dotSep I
undSep amAManTest
dotSep amAManTest
camel AMan
camel Test
-----------
Test
amAManTest
I_amAManTest
I
AMan
###########
word you_haveAHouse
undSep you
dotSep you
undSep haveAHouse
dotSep haveAHouse
camel AHouse
-----------
you
you_haveAHouse
haveAHouse
AHouse
###########
word FOO_BAR_test.model
undSep FOO
dotSep FOO
undSep BAR
dotSep BAR
undSep test.model
dotSep test
dotSep model
-----------
model
FOO
FOO_BAR_test.model
test.model
BAR
test
###########

관련 정보