División de archivos de texto basados ​​en una expresión regular

16

Tengo un archivo de texto que quiero dividir en 64 partes desiguales, de acuerdo con los 64 hexagramas del Yi Jing. Como el pasaje para cada hexagrama comienza con algunos dígitos, un punto y dos líneas nuevas, la expresión regular debería ser bastante fácil de escribir.

Pero, ¿cómo divido el archivo de texto en 64 archivos nuevos de acuerdo con esta expresión regular? Parece más una tarea para perl. Pero tal vez hay una forma más obvia de que me estoy perdiendo por completo.

ixtmixilix
fuente

Respuestas:

23

Esto sería csplitexcepto que la expresión regular debe ser una sola línea. Eso también hace seddifícil; Yo iría con Perl o Python.

Podrías ver si

csplit foo.txt '/^[0-9][0-9]*\.$/' '{64}'

es lo suficientemente bueno para tus propósitos. ( csplitrequiere un POSIX BRE, por lo que no se puede usar \do +, entre otros).

geekosaur
fuente
Gracias, @geekosaur. Funcionó perfectamente, aunque tuve que cambiarlo a {63}.
ixtmixilix
1
Entonces, '\.'¿no funcionará también?
Vanuan
4

Creo que la mejor manera es awky gawk.

awk

awk -F "([.] )|( / )" '/^[0-9]{1,3}[.]/{x="F"$1"("$2").txt";}{print >x;}' I_Ching_Wilhelm_Translation.txt

-Fespecificará los campos separadores para cada línea. Es una expresión regular, aquí usamos múltiples separadores: ". "y " / ". Por lo tanto, una línea como 1. Ch'ien / The Creativese dividirá en 3 campos: 1 Ch'ieny The Creative. Más adelante podemos referirnos a estos campos con $n. $0Es toda la línea.

Luego le decimos a awk que combine las líneas con el patrón. ^[0-9]{1,3}[.]Si hay una coincidencia, le asignamos un valor x. El valor x se usará como nombre de archivo para la printoperación. En este ejemplo usamos "F"$1"("$2").txt"para que la línea 1. Ch'ien / The Creativedé un nombre de archivoF1(Ch'ien).txt

papar moscas

En gawk, también podemos acceder al grupo capturado. Entonces podemos simplificar el comando para:

gawk 'match($0, /^([0-9]{1,3})[.] (.*) \/ (.*)$/, ary){x="F"ary[1]"("ary[2]")";}{print >x;}' I_Ching_Wilhelm_Translation.txt

Aquí usamos matchla captura de los grupos y los colocamos en una lista variable ary. $0Es toda la línea. ary[0]es todo igual ary[1...n]es cada grupo

perl

También podemos hacerlo con perl:

perl -ne 'if(/^([0-9]{1,3})[.] (.*) \/ (.*)$/) {close F; open F, ">", sprintf("F$1($2).txt");} print F' I_Ching_Wilhelm_Translation.txt

Resultados:

> ls F*
F10(Lü).txt         F22(Pi).txt       F34(Ta Chuang).txt  F46(Shêng).txt     F58(Tui).txt
F11(T'ai).txt       F23(Po).txt       F35(Chin).txt       F47(K'un).txt      F59(Huan).txt
F12(P'i).txt        F24(Fu).txt       F36(Ming I).txt     F48(Ching).txt     F5(Hsü).txt
F13(T'ung Jên).txt  F25(Wu Wang).txt  F37(Chia Jên).txt   F49(Ko).txt        F60(Chieh).txt
F14(Ta Yu).txt      F26(Ta Ch'u).txt  F38(K'uei).txt      F4(Mêng).txt       F61(Chung Fu).txt
F15(Ch'ien).txt     F27(I).txt        F39(Chien).txt      F50(Ting).txt      F62(Hsiao Kuo).txt
F16(Yü).txt         F28(Ta Kuo).txt   F3(Chun).txt        F51(Chên).txt      F63(Chi Chi).txt
F17(Sui).txt        F29(K'an).txt     F40(Hsieh).txt      F52(Kên).txt       F64(Wei Chi).txt
F18(Ku).txt         F2(K'un).txt      F41(Sun).txt        F53(Chien).txt     F6(Sung).txt
F19(Lin).txt        F30(Li).txt       F42(I).txt          F54(Kuei Mei).txt  F7(Shih).txt
F1(Ch'ien).txt      F31(Hsien).txt    F43(Kuai).txt       F55(Fêng).txt      F8(Pi).txt
F20(Kuan).txt       F32(Hêng).txt     F44(Kou).txt        F56(Lü).txt        F9(Hsiao Ch'u).txt
F21(Shih Ho).txt    F33(TUN).txt      F45(Ts'ui).txt      F57(Sun).txt

cómo obtener el archivo de ejemplo:

curl http://www2.unipr.it/~deyoung/I_Ching_Wilhelm_Translation.html|html2text -o I_Ching_Wilhelm_Translation.plain
sed 's|^[[:blank:]]*||g' I_Ching_Wilhelm_Translation.plain > I_Ching_Wilhelm_Translation.txt
Wang
fuente
3

Con GNU coreutils, puede usar csplitpara dividir un archivo en partes delimitadas por expresiones regulares , como lo muestra geekosaur .

Aquí hay un script awk portátil para dividir un archivo en pedazos. Funciona por

  • llamando getlinepara tratar con el separador multilínea (2 líneas);
  • establecer una variable outfileal nombre del archivo para imprimir, cuando se encuentra un encabezado de sección.
BEGIN {outfile="header.txt"}
{
    while (/^[0-9]+\.$/) {
        prev = $0; getline;
        if ($0 == "") outfile = prev "txt";
        print prev >outfile
    }
    print >outfile
}
Gilles 'SO- deja de ser malvado'
fuente
Esto funciona en principio , pero el encabezado de sección de los datos reales de la página web no está representado por la expresión regular (del mismo modo que con la respuesta de geekosaur). Al inicio nunber. le sigue un texto que contiene una barra inclinada /. Estoy bastante seguro de que el two newlines ixtmixilix mencionado son las 2 líneas en blanco que preceden al identificador numérico e identificarían más específicamente el encabezado, pero como los datos en la página web solo coinciden /^[0-9]+\. en los encabezados de sección, no hay necesidad de atenderlos ( en este caso particular) Gracias; especialmente para la introducción a getline... PS. puede ser si?
Peter
@fred geekosaur y yo seguimos la descripción de la pregunta, no los datos del sitio web. El diseño dependerá del motor de representación HTML utilizado para convertir a texto; la parte en la que esto se representa desde una página web es realmente irrelevante para la pregunta. ||| whileestá allí en caso de que la entrada contenga 1.\n2.\n\n(donde \nhay líneas nuevas): 2.debe reconocerse en la línea del encabezado. Aquí no va a ocurrir, pero lo apoyo en mi código para hacerlo más general (y para que coincida con la especificación de la pregunta más estrictamente).
Gilles 'SO- deja de ser malvado'