Eu tenho um fluxo de texto ou arquivo que contém palavras separadas por espaços em branco. Como:
I have a toy. you may not like it.
Cada palavra separada por espaços em branco pode ser composta por duas ou mais palavras pequenas, talvez organizadas por caixa de camelo (separadas por caixa diferente), caixa de cobra (separada por sublinhado) ou separada por ponto, como:
I_amAManTest you_haveAHouse FOO_BAR_test.model
por exemplo:
I_amAManTest
pode ser dividido em:
I
am
A
Man
Test
mas quero imprimir todosnpalavras (cada subconjunto de pequenas palavras contíguas) na palavra composta, como:
I_amAManTest
saída:
// 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
Então, em conclusão, para entradas como
I_amAManTest you_haveAHouse FOO_BAR_test
a saída deve ser
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
Responder1
Uma solução (principalmente) 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'
O algoritmo é
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
Detalhes:
cat "$@"
é um UUOC. Geralmente evito isso; você pode fazer , mas não pode passar vários arquivos diretamente.tr args < file
tr
tr -cs -- '._[:alpha:]' '[\n*]'
quebra uma linha de muitas palavras compostas em linhas separadas; por exemplo,
torna-seI_amAManTest you_haveAHouse FOO_BAR_test
então sed pode processar uma palavra composta por vez.I_amAManTest you_haveAHouse FOO_BAR_test
sed -n
— não imprima nada automaticamente; imprimir apenas quando solicitado.-e
especifica que o seguinteexpression faz parte do script sed.h
— copie o espaço padrão para o espaço de espera.:ms
— um rótulo (Main loop Start)p
- imprimir:ss
— um rótulo (início do loop secundário)- Os comandos a seguir removem uma palavra pequena do final de uma palavra composta e, se forem bem-sucedidos, imprimem o resultado e voltam ao início do loop Secundário.
s/\([[:lower:]]\)[[:upper:]][[:lower:]]*$/\1/p; t ss
— altera “nTest” para “n”.s/\([[:lower:]]\)[[:upper:]][[:upper:]]*$/\1/p; t ss
— altera “mOK” para “m”.s/\([[:upper:]]\)[[:upper:]][[:lower:]]\+$/\1/p; t ss
— muda “AMan” para “A”.s/[._][[:alpha:]][[:lower:]]*$//p; t ss
— exclui “_am” (substitui por nada).s/[._][[:upper:]]\+$//p; t ss
— exclui “_BAR” (substitui por nada).
- Este é o fim do loop secundário.
g
— copie o espaço de espera para o espaço do padrão (volte ao que você tinha no início do loop acima).- Os comandos a seguir removem uma palavra pequena do início de uma palavra composta e, se bem-sucedidos, saltam para o final do loop principal (mw = encerramento do loop principal).
s/^[[:upper:]]\?[[:lower:]]\+\([[:upper:]]\)/\1/; t mw
— muda “amA” para “A” e “ManT” para “T”.s/^[[:upper:]]\+\([[:upper:]][[:lower:]]\)/\1/; t mw
— muda “AMa” para “Ma”.s/^[[:alpha:]][[:lower:]]*[._]//; t mw
— exclui “I_” e “you_” (substitui-os por nada).s/^[[:upper:]]\+[._]//; t mw
— exclui “FOO_” (substitui por nada).- Cada um dos comandos substitutos acima salta para o encerramento do loop principal (abaixo) se for bem-sucedido (se encontrar/corresponder a algo). Se chegarmos aqui, o espaço padrão contém apenas uma pequena palavra, então terminamos.
b
— ramifica (salta) para o final do script sed; ou seja, saia do script sed.:mw
— rótulo para encerramento do loop principal.h
- copie o espaço padrão para o espaço de espera, para nos prepararmos para a próxima iteração do loop principal.b ms
— salte para o início do loop principal.
Ele produz a saída solicitada. Infelizmente, isso coloca tudo em uma ordem diferente. Provavelmente posso consertar isso se for importante.
$ 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
Responder2
Sua melhor aposta provavelmente será encontrar um módulo tokenizador para Perl. O Grep não pode fazer isso sem várias execuções, provavelmente precisando de -P
(PCRE).
Aqui está uma solução parcial sem nenhum módulo 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
}
}
Lê entradas de entradas ou arquivos padrão, uma linha por vez. $n
é um contador de palavras para o comentário impresso, então iteramos através das palavras (conforme delineado por espaço em branco, portanto, a regex /(\S+)/g
corresponde globalmente a caracteres consecutivos que não sejam de espaço em branco). Dentro de cada palavra, iteramos nas partes do token usando([a-zA-Z0-9][a-z]*+)
, cujas correspondências começam com números ou letras e são seguidas por zero ou mais letras minúsculas ( *+
é como *
se o retrocesso fosse desativado para proteção contra umReDoS). Depois de imprimir todos os tokens correspondentes na palavra, imprimimos a palavra inteira.
Você executa isso como perl solution.pl intput.txt
ou então inline como:
$ 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
Observe que faltam os subtokens de palavras com várias partes.
Observe também que sua solicitação para I_AmAMan
analisar como I
, Am
, A
, Man
entra em conflito com sua solicitação para FOO_BAR
analisar FOO
, BAR
em vez de F
, O
, O
, B
... como faz o código acima. (Talvez um exemplo melhor seria: o que deveria I_AmOK
ser? Três unigramas ou quatro?)
Responder3
Aqui está um começo, você só terá que massageá-lo depois de descobrir seus requisitos para strings que contêm misturas de letras maiúsculas e minúsculas e imprimir a saída na ordem que você está mostrando em sua pergunta:
$ 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
###########