Разделение текстовых файлов на основе регулярного выражения

16

У меня есть текстовый файл, который я хочу разделить на 64 неравные части, в соответствии с 64 гексаграммами И Цзин. Поскольку отрывок для каждой гексаграммы начинается с некоторой цифры (цифр), точки и двух новых строк, регулярное выражение должно быть довольно простым для написания.

Но как на самом деле разделить текстовый файл на 64 новых файла в соответствии с этим регулярным выражением? Это кажется более сложной задачей perl. Но, может быть, есть более очевидный способ, который я просто пропускаю.

ixtmixilix
источник

Ответы:

23

Это было бы csplitза исключением того, что регулярное выражение должно быть одной строкой. Это также sedзатрудняет; Я бы пошел с Perl или Python.

Вы могли видеть, если

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

достаточно хорош для ваших целей. ( csplitтребует POSIX BRE, поэтому он не может использовать \dили +, среди прочего.)

geekosaur
источник
Спасибо, @geekosaur. Он работал отлично, хотя мне пришлось изменить его на {63}.
ixtmixilix
1
Значит, '\.'тоже не сработает?
Вануан
4

Я думаю, что лучший способ awkи gawk.

AWK

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

-Fбудет указывать поля разделитель для каждой строки. Это регулярное выражение, здесь мы используем несколько разделителей: ". "и " / ". Таким образом строка вроде 1. Ch'ien / The Creativeбудет разбита на 3 поля: 1 Ch'ienи The Creative. Позже мы можем ссылаться на эти поля с $n. $0это вся линия.

Затем ^[0-9]{1,3}[.]мы сообщаем awk о соответствии строк шаблону. Если есть соответствие, мы присваиваем значение x. Значение x будет использоваться в качестве имени файла для printоперации. В этом примере мы используем, "F"$1"("$2").txt"чтобы строка 1. Ch'ien / The Creativeдала имя файлаF1(Ch'ien).txt

простак

В gawk мы также можем получить доступ к захваченной группе. Таким образом, мы можем упростить команду для:

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

здесь мы используем matchзахват групп и помещаем их в список переменных ary. $0это вся линия. ary[0]все соответствует. ary[1...n]это каждая группа.

Perl

Мы также можем сделать это с помощью Perl:

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

Результаты:

> 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

как получить файл примера:

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

С помощью GNU coreutils вы можете использовать, csplitчтобы разбить файл на части, разделенные регулярным выражением, как показано geekosaur .

Вот портативный awk-скрипт для разбиения файла на части. Это работает

  • вызов getlineдля работы с многострочным (2-строчным) разделителем;
  • установка переменной outfileдля имени файла для печати, когда встречается заголовок раздела.
BEGIN {outfile="header.txt"}
{
    while (/^[0-9]+\.$/) {
        prev = $0; getline;
        if ($0 == "") outfile = prev "txt";
        print prev >outfile
    }
    print >outfile
}
Жиль "ТАК - прекрати быть злым"
источник
Это работает в принципе , но заголовок раздела фактических данных веб-страницы не так, как представлено регулярным выражением (аналогично с ответом geekosaur). За первым nunber. следует текст, который содержит косую черту /. Я почти уверен, что упомянутый two newlines ixtmixilix - это две пустые строки, которые предшествуют числовому идентификатору и более конкретно идентифицируют заголовок, но, поскольку данные на веб-странице совпадают только /^[0-9]+\. в заголовках разделов, нет необходимости их обслуживать ( в данном конкретном случае). Благодарность; специально для вступления getline.. PS. может быть, если?
Peter.O
@fred geekosaur и я пошли по описанию в вопросе, а не по данным на сайте. Макет будет зависеть от механизма рендеринга HTML, используемого для преобразования в текст; та часть, где это отображается на веб-странице, на самом деле не имеет отношения к вопросу. ||| whileесть ли в случае, если вход содержит 1.\n2.\n\n(где \nновые строки): 2.должны быть распознаны в строке заголовка. Это не произойдет здесь, но я поддерживаю это в своем коде, чтобы сделать его более общим (и более точно соответствовать спецификации в вопросе).
Жиль "ТАК - перестань быть злым"