Dzielenie plików tekstowych na podstawie wyrażeń regularnych

16

Mam plik tekstowy, który chcę podzielić na 64 nierówne części, zgodnie z 64 heksagramami Yi Jing. Ponieważ fragment każdego heksagramu zaczyna się cyfrą (kropkami), kropką i dwiema znakami nowego wiersza, wyrażenie regularne powinno być dość łatwe do napisania.

Ale jak właściwie podzielić plik tekstowy na 64 nowe pliki zgodnie z tym wyrażeniem regularnym? To wydaje się bardziej zadaniem perl. Ale może istnieje bardziej oczywisty sposób, że po prostu całkowicie mi brakuje.

ixtmixilix
źródło

Odpowiedzi:

23

Byłoby to csplitwyjątkiem tego, że wyrażenie regularne musi być pojedynczą linią. To również sedutrudnia; Wybrałbym Perla lub Pythona.

Możesz zobaczyć, czy

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

jest wystarczająco dobry do twoich celów. ( csplitwymaga POSIX BRE, więc nie można go używać \dani +, między innymi).

geekozaur
źródło
Dzięki, @geekosaur. Działa idealnie, ale musiałem to zmienić na {63}.
ixtmixilix
1
Więc też '\.'nie będzie działać?
Vanuan
4

Myślę, że najlepszym sposobem jest awki gawk.

awk

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

-Fokreśli separator pól dla każdej linii. Jest to wyrażenie regularne, tutaj używamy wielu separatorów: ". "i " / ". W ten sposób podobny wiersz 1. Ch'ien / The Creativezostanie podzielony na 3 pola: 1 Ch'ieni The Creative. Później możemy odwoływać się do tych pól za pomocą $n. $0to cała linia.

Następnie mówimy awk, aby dopasował linie do wzoru. ^[0-9]{1,3}[.]Jeśli istnieje dopasowanie, wówczas przypisujemy wartość do x. Wartość x zostanie użyta jako nazwa pliku do printoperacji. W tym przykładzie używamy, "F"$1"("$2").txt"więc linia 1. Ch'ien / The Creativepodaje nazwę plikuF1(Ch'ien).txt

gapić się

W gawk możemy również uzyskać dostęp do przechwyconej grupy. Możemy więc uprościć polecenie, aby:

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

tutaj używamy matchprzechwytywania grup i umieszczamy je na liście zmiennych ary. $0to cała linia. ary[0]wszystko jest dopasowane. ary[1...n]jest każda grupa.

perl

Możemy to również zrobić za pomocą Perla:

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

Wyniki:

> 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

jak zdobyć przykładowy plik:

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
źródło
3

W coreutils GNU możesz użyć csplitdo rozbicia pliku na fragmenty wyrażone wyrażeniami regularnymi , jak pokazuje geekozaur .

Oto przenośny skrypt awk do rozbicia pliku na części. Działa przez

  • wzywając getlinedo obsługi separatora wieloliniowego (2-liniowego);
  • ustawienie zmiennej outfilena nazwę pliku do wydrukowania, gdy napotkasz nagłówek sekcji.
BEGIN {outfile="header.txt"}
{
    while (/^[0-9]+\.$/) {
        prev = $0; getline;
        if ($0 == "") outfile = prev "txt";
        print prev >outfile
    }
    print >outfile
}
Gilles „SO- przestań być zły”
źródło
Działa to w zasadzie , ale nagłówek sekcji rzeczywistych danych strony internetowej nie jest reprezentowany przez regex (podobnie z odpowiedzią geekozaura). Po wiodącym nunber. następuje tekst zawierający ukośnik /. Jestem prawie pewien, że wspomniany two newlines ixtmixilix to 2 puste wiersze poprzedzające identyfikator numeryczny i dokładniej identyfikujące nagłówek, ale ponieważ dane na stronie internetowej pasują tylko /^[0-9]+\. do nagłówków sekcji, nie trzeba ich uwzględniać ( w tym konkretnym przypadku). dzięki; szczególnie za wprowadzenie do getline... PS. może być, jeśli?
Peter.O
@fred geekosaur i ja poszliśmy według opisu w pytaniu, a nie danych na stronie. Układ będzie zależał od silnika renderowania HTML używanego do konwersji na tekst; część, w której jest to renderowane ze strony internetowej, nie ma znaczenia dla pytania. ||| whileistnieje w przypadku, gdy dane wejściowe zawierają 1.\n2.\n\n(gdzie \nsą znaki nowego wiersza): 2.należy je rozpoznać w wierszu nagłówka. Nie będzie się tutaj pojawiać, ale popieram go w moim kodzie, aby był bardziej ogólny (i ściślej pasował do specyfikacji w pytaniu).
Gilles „SO - przestań być zły”