Aufteilen von Textdateien basierend auf einem regulären Ausdruck

15

Ich habe eine Textdatei, die ich nach den 64 Hexagrammen des Yi Jing in 64 ungleiche Teile aufteilen möchte. Da die Passage für jedes Hexagramm mit einigen Ziffern, einem Punkt und zwei Zeilenumbrüchen beginnt, sollte der reguläre Ausdruck ziemlich einfach zu schreiben sein.

Aber wie teile ich die Textdatei gemäß dieser Regex tatsächlich in 64 neue Dateien auf? Es scheint eher eine Aufgabe für perl. Aber vielleicht gibt es einen offensichtlicheren Weg, den ich einfach total vermisse.

ixtmixilix
quelle

Antworten:

22

Dies würde csplitbedeuten, dass der reguläre Ausdruck eine einzelne Zeile sein muss. Das macht auch sedschwierig; Ich würde mit Perl oder Python gehen.

Sie könnten sehen, ob

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

ist gut genug für Ihre Zwecke. ( csplitBenötigt ein POSIX-BRE, kann also unter anderem \doder nicht verwenden +.)

Geekosaurier
quelle
Danke, @geekosaur. Es hat perfekt funktioniert, obwohl ich es auf {63} ändern musste.
ixtmixilix
1
Funktioniert '\.'das nicht auch?
Vanuan
4

Ich denke der beste Weg ist awkund gawk.

awk

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

-FGibt für jede Zeile einen Feldtrenner an. Es ist ein regulärer Ausdruck, hier verwenden wir mehrere Trennzeichen: ". "und " / ". So wird eine Zeile wie 1. Ch'ien / The Creativein 3 Felder aufgeteilt: 1 Ch'ienund The Creative. Später können wir auf diese Felder mit verweisen $n. $0ist die gesamte Zeile.

Wir ^[0-9]{1,3}[.]weisen die awk dann an, die Linien mit dem Muster abzugleichen. Wenn es eine Übereinstimmung gibt, weisen wir Wert zu x. Der Wert x wird als Dateiname für die printOperation verwendet. In diesem Beispiel geben wir in "F"$1"("$2").txt"der Zeile 1. Ch'ien / The Creativeeinen Dateinamen anF1(Ch'ien).txt

gaffen

In Gawk können wir auch auf gefangene Gruppen zugreifen. So können wir den Befehl vereinfachen, um:

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

hier verwenden wir matchdas erfassen der gruppen und fügen sie in die variable liste ein ary. $0ist die gesamte Zeile. ary[0]ist alles abgestimmt. ary[1...n]ist jede Gruppe.

perl

Wir können es auch mit Perl machen:

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

Ergebnisse:

> 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

So erhalten Sie die Beispieldatei:

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
quelle
3

Mit GNU coreutils können Sie cspliteine Datei in durch Regexp getrennte Teile aufteilen, wie von Geekosaurier gezeigt .

Hier ist ein portables awk-Skript, um eine Datei in Teile zu zerlegen. Es funktioniert von

  • Aufrufen getline, um mit dem mehrzeiligen (2-zeiligen) Trennzeichen umzugehen;
  • Setzen einer Variablen outfileauf den Namen der Datei, auf die gedruckt werden soll, wenn eine Abschnittsüberschrift auftritt.
BEGIN {outfile="header.txt"}
{
    while (/^[0-9]+\.$/) {
        prev = $0; getline;
        if ($0 == "") outfile = prev "txt";
        print prev >outfile
    }
    print >outfile
}
Gilles 'SO - hör auf böse zu sein'
quelle
Dies funktioniert im Prinzip , aber der Abschnittsheader der eigentlichen Webseiten-Daten entspricht nicht dem regulären Ausdruck (ebenfalls mit der Antwort von Geekosaurier). Dem führenden nunber. folgt Text, der einen Schrägstrich enthält /. Ich bin mir ziemlich sicher, dass das erwähnte two newlines ixtmixilix die 2 Leerzeilen sind , die der numerischen Kennung vorangehen und die Überschrift genauer identifizieren, aber da die Daten auf der Webseite nur /^[0-9]+\. in den Abschnittsüberschriften übereinstimmen , müssen sie nicht berücksichtigt werden ( in diesem speziellen Fall). Vielen Dank; vor allem für das Intro zu getline.. PS. kann dabei sein wenn?
Peter.O
@fred geekosaur und ich gingen bei der Beschreibung auf die Frage ein, nicht auf die Daten auf der Webseite. Das Layout hängt von der HTML-Rendering-Engine ab, die zum Konvertieren in Text verwendet wird. Der Teil, in dem dies von einer Webseite gerendert wird, ist für die Frage eigentlich irrelevant. ||| Ist whileda, falls die Eingabe enthält 1.\n2.\n\n(wo \nsind Zeilenumbrüche): Das 2.muss in der Kopfzeile erkannt werden. Es wird hier nicht vorkommen, aber ich unterstütze es in meinem Code, um es allgemeiner zu gestalten (und der Spezifikation in der Frage genauer zu entsprechen).
Gilles 'SO- hör auf böse zu sein'