Tengo una secuencia de texto o un archivo que contiene palabras separadas por espacios en blanco. Como:
I have a toy. you may not like it.
Cada palabra separada por espacios en blanco puede estar compuesta por dos o más palabras pequeñas, tal vez organizadas por mayúsculas y minúsculas (separadas por mayúsculas y minúsculas), mayúsculas y minúsculas (separadas por subrayado) o separadas por puntos, como:
I_amAManTest you_haveAHouse FOO_BAR_test.model
Por ejemplo:
I_amAManTest
se puede dividir en:
I
am
A
Man
Test
pero quiero imprimir cadanortepalabras (cada subconjunto de palabras pequeñas contiguas) en la palabra compuesta, como:
I_amAManTest
producción:
// 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
En conclusión, para entradas como
I_amAManTest you_haveAHouse FOO_BAR_test
la salida debe 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
Respuesta1
Una solución (en su mayoría) 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'
El algoritmo es
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
Detalles:
cat "$@"
es un UUOC. Normalmente los evito; Puedes hacerlo , pero no puedes pasar varios archivos directamente.tr args < file
tr
tr -cs -- '._[:alpha:]' '[\n*]'
divide una línea de muchas palabras compuestas en líneas separadas; p.ej,
se convierteI_amAManTest you_haveAHouse FOO_BAR_test
entonces sed puede procesar una palabra compuesta a la vez.I_amAManTest you_haveAHouse FOO_BAR_test
sed -n
— no imprimir nada automáticamente; imprimir sólo cuando se le ordene.-e
especifica que lo siguientemixpression es parte del script sed.h
— copie el espacio del patrón en el espacio de espera.:ms
— una etiqueta (Inicio del bucle principal)p
- imprimir:ss
— una etiqueta (inicio del bucle secundario)- Los siguientes comandos eliminan una palabra pequeña del final de una palabra compuesta y, si tienen éxito, imprimen el resultado y regresan al principio del ciclo secundario.
s/\([[:lower:]]\)[[:upper:]][[:lower:]]*$/\1/p; t ss
— cambia “nTest” a “n”.s/\([[:lower:]]\)[[:upper:]][[:upper:]]*$/\1/p; t ss
— cambia “mOK” a “m”.s/\([[:upper:]]\)[[:upper:]][[:lower:]]\+$/\1/p; t ss
— cambia “AMan” a “A”.s/[._][[:alpha:]][[:lower:]]*$//p; t ss
— elimina “_am” (lo reemplaza por nada).s/[._][[:upper:]]\+$//p; t ss
— elimina “_BAR” (lo reemplaza por nada).
- Este es el final del ciclo secundario.
g
— copia el espacio de espera al espacio del patrón (vuelve a lo que tenías al principio del bucle anterior).- Los siguientes comandos eliminan una palabra pequeña del principio de una palabra compuesta y, si tienen éxito, saltan hasta el final del bucle principal (mw = Resumen del bucle principal).
s/^[[:upper:]]\?[[:lower:]]\+\([[:upper:]]\)/\1/; t mw
— cambia “amA” a “A” y “ManT” a “T”.s/^[[:upper:]]\+\([[:upper:]][[:lower:]]\)/\1/; t mw
— cambia “AMa” a “Ma”.s/^[[:alpha:]][[:lower:]]*[._]//; t mw
— elimina “I_” y “you_” (los reemplaza por nada).s/^[[:upper:]]\+[._]//; t mw
— elimina “FOO_” (lo reemplaza por nada).- Cada uno de los comandos sustitutos anteriores salta al resumen del bucle principal (a continuación) si tiene éxito (si encuentra/coincide con algo). Si llegamos aquí, el espacio del patrón contiene solo una palabra pequeña, así que terminamos.
b
— saltar (saltar) hasta el final del script sed; es decir, salir del script sed.:mw
— etiqueta para resumen del bucle principal.h
— copie el espacio del patrón en el espacio de retención, para prepararnos para la siguiente iteración del bucle principal.b ms
— salta al principio del bucle principal.
Produce la salida solicitada. Desafortunadamente, lo pone en un orden diferente. Probablemente pueda arreglar eso si es 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
Respuesta2
Es probable que su mejor opción sea encontrar un módulo tokenizador para Perl. Grep no puede hacer esto sin varias ejecuciones, probablemente necesite -P
(PCRE).
Aquí hay una solución parcial sin ningún 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
}
}
Esto lee entradas de archivos o entradas estándar, una línea a la vez. $n
es un contador de palabras para el comentario impreso, luego iteramos a través de las palabras (según lo delineado por espacios en blanco, por lo tanto, la expresión regular /(\S+)/g
coincide globalmente con caracteres consecutivos que no son espacios en blanco). Dentro de cada palabra, iteramos sobre las partes del token usando([a-zA-Z0-9][a-z]*+)
, cuyas coincidencias comienzan todas con números o letras y van seguidas de cero o más letras minúsculas ( *+
es como *
tener el retroceso desactivado para protegerse contra unRehacer). Después de imprimir todas las fichas coincidentes en la palabra, imprimimos la palabra completa.
Ejecute esto como perl solution.pl intput.txt
o en línea 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
Tenga en cuenta que a esto le faltan los subtokens de palabras de varias partes.
También tenga en cuenta que su solicitud de I_AmAMan
analizar como I
, Am
, entra A
en Man
conflicto con su solicitud de FOO_BAR
analizar en FOO
, BAR
en lugar de F
, O
, O
, B
... como lo hace el código anterior. (Quizás un mejor ejemplo sería: ¿qué debería I_AmOK
ser? ¿Tres unigramos o cuatro?)
Respuesta3
Este es un comienzo, solo tendrá que modificarlo una vez que determine sus requisitos para las cadenas que contienen mezclas de letras mayúsculas y minúsculas e imprimir el resultado en cualquier orden que esté mostrando en su pregunta:
$ 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
###########