我有一个文本流或一个文件,其中包含由空格分隔的单词。喜欢:
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 < file
tr
tr -cs -- '._[:alpha:]' '[\n*]'
将一行许多复合词分成单独的行;例如,
变成I_amAManTest you_haveAHouse FOO_BAR_test
因此 sed 一次可以处理一个复合词。I_amAManTest you_haveAHouse FOO_BAR_test
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_BAR
FOO
BAR
F
O
O
B
I_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
###########