一个 shell 命令即可查找文本中的每个 n 元语法

一个 shell 命令即可查找文本中的每个 n 元语法

我有一个文本流或一个文件,其中包含由空格分隔的单词。喜欢:

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指定以下内容expression 是 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 就无法做到这一点,可能需要-P(PCRE)。

这是没有任何 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]*+),其匹配项全部以数字或字母开头,后跟零个或多个小写字母(*+就像*禁用回溯以防止重做服务)。在打印单词中所有匹配的标记后,我们打印整个单词。

您可以将其运行为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另请注意,您的解析为I, Am, A,的请求与解析为,Man的请求冲突,而不是像上面的代码那样解析为, , , ... 。 (也许更好的例子是:应该变成什么?三个一元词还是四个?)FOO_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
###########

相关内容