Dividere i file di testo in base a un'espressione regolare


16

Ho un file di testo che voglio dividere in 64 parti disuguali, secondo i 64 esagrammi dello Yi Jing. Poiché il passaggio per ciascun esagramma inizia con alcune cifre, un punto e due nuove righe, la regex dovrebbe essere abbastanza facile da scrivere.

Ma come posso effettivamente dividere il file di testo in 64 nuovi file secondo questa regex? Sembra più un compito per perl. Ma forse c'è un modo più ovvio che mi manchi completamente.

Risposte:


23

Ciò significherebbe csplitche il regex deve essere una riga singola. Anche questo rende seddifficile; Andrei con Perl o Python.

Potresti vedere se

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

è abbastanza buono per i tuoi scopi. ( csplitrichiede un POSIX BRE, quindi non può essere utilizzato \do +, tra gli altri.)


Grazie @geekosaur. Ha funzionato perfettamente, anche se ho dovuto cambiarlo in {63}.
ixtmixilix,

1
Quindi, '\.'non funzionerà troppo?
Vanuan,

4

Penso che il modo migliore sia awke gawk.

awk

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

-Fspecificherà i campi separatore per ogni riga. È una regex, qui usiamo più separatori: ". "e " / ". Quindi una linea come 1. Ch'ien / The Creativesarà divisa in 3 campi: 1 Ch'iene The Creative. Successivamente possiamo fare riferimento a questi campi con $n. $0è l'intera linea.

Quindi diciamo a awk di abbinare le linee al modello ^[0-9]{1,3}[.]Se c'è una corrispondenza, assegniamo un valore a x. Il valore x verrà utilizzato come nome file per l' printoperazione. In questo esempio usiamo "F"$1"("$2").txt"quindi la riga 1. Ch'ien / The Creativefornisce un nome fileF1(Ch'ien).txt

allocco

In gawk, possiamo anche accedere al gruppo acquisito. Quindi possiamo semplificare il comando per:

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

qui utilizziamo matchl'acquisizione dei gruppi e li inseriamo nell'elenco delle variabili ary. $0è l'intera linea. ary[0]è tutto abbinato. ary[1...n]è ogni gruppo.

perl

Possiamo anche farlo 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

risultati:

> 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

come ottenere il file di esempio:

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

3

Con i coreutils GNU, puoi usare csplitper spezzare un file in pezzi delimitati da regexp, come mostrato da geekosaur .

Ecco uno script awk portatile per suddividere un file in pezzi. Funziona da

  • chiamando getlineper occuparsi del separatore multilinea (a 2 righe);
  • impostando una variabile outfilesul nome del file su cui stampare, quando viene rilevata un'intestazione di sezione.
BEGIN {outfile="header.txt"}
{
    while (/^[0-9]+\.$/) {
        prev = $0; getline;
        if ($0 == "") outfile = prev "txt";
        print prev >outfile
    }
    print >outfile
}

Funziona in linea di principio , ma l'intestazione della sezione dei dati della pagina Web effettiva non è rappresentata dal regex (allo stesso modo con la risposta di geekosaur). Il primo nunber. è seguito da un testo che contiene una barra /. Sono abbastanza sicuro che two newlines ixtmixilix menzionato siano le 2 righe vuote che precedono l'identificatore numerico e identificherebbero in modo più specifico l'intestazione, ma poiché i dati sulla pagina web corrispondono solo /^[0-9]+\. nelle intestazioni di sezione, non è necessario soddisfarli ( in questo caso particolare). Grazie; soprattutto per l'introduzione di getline.. PS. mentre può essere se?
Peter

@fred geekosaur e io siamo andati dalla descrizione nella domanda, non dai dati sul sito web. Il layout dipenderà dal motore di rendering HTML utilizzato per la conversione in testo; la parte in cui questo è reso da una pagina web è in realtà irrilevante per la domanda. ||| whilec'è nel caso in cui l'input contenga 1.\n2.\n\n(dove \nsono le righe): il 2.deve essere riconosciuto nella riga di intestazione. Non si verificherà qui, ma lo supporto nel mio codice per renderlo più generale (e abbinare le specifiche nella domanda più rigorosamente).
Gilles 'SO- smetti di essere malvagio' il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.