我有一個文字流或一個文件,其中包含由空格分隔的單字。喜歡:
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
###########