Tách các tệp văn bản dựa trên biểu thức chính quy


16

Tôi có một tệp văn bản mà tôi muốn chia thành 64 phần không bằng nhau, theo 64 quẻ của Yi Jing. Vì đoạn văn cho mỗi quẻ bắt đầu bằng một số chữ số, một khoảng thời gian và hai dòng mới, regex nên khá dễ viết.

Nhưng làm thế nào để tôi thực sự chia tệp văn bản thành 64 tệp mới theo biểu thức chính quy này? Có vẻ như nhiều hơn một nhiệm vụ cho perl. Nhưng có lẽ có một cách rõ ràng hơn mà tôi hoàn toàn thiếu.

Câu trả lời:


23

Điều này sẽ được csplitngoại trừ rằng regex phải là một dòng duy nhất. Điều đó cũng sedgây khó khăn; Tôi sẽ đi với Perl hoặc Python.

Bạn có thể thấy nếu

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

là đủ tốt cho mục đích của bạn. ( csplityêu cầu BREIX BRE, vì vậy nó không thể sử dụng \dhoặc +, trong số những người khác.)


Cảm ơn, @geekizard. Nó hoạt động hoàn hảo, mặc dù tôi phải đổi nó thành {63}.
ixtmixilix

1
Vì vậy, '\.'sẽ không làm việc quá?
Vanuan

4

Tôi nghĩ rằng cách tốt nhất là awkgawk.

ôi

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

-Fsẽ chỉ định các trường tách biệt cho mỗi dòng. Đây là một regex, ở đây chúng tôi sử dụng nhiều bộ tách: ". "" / ". Do đó, một dòng như 1. Ch'ien / The Creativesẽ được chia thành 3 trường: 1 Ch'ienThe Creative. Sau này chúng ta có thể tham khảo các lĩnh vực này với $n. $0là toàn bộ dòng.

Sau đó, chúng tôi nói với awk để khớp các dòng với mẫu ^[0-9]{1,3}[.]Nếu có khớp, sau đó chúng ta gán giá trị cho x. Giá trị x sẽ được sử dụng làm tên tệp cho printhoạt động. Trong ví dụ này, chúng tôi sử dụng "F"$1"("$2").txt"để dòng 1. Ch'ien / The Creativecho một tên tệpF1(Ch'ien).txt

chim ưng

Trong gawk, chúng tôi cũng có thể truy cập nhóm bị bắt. Vì vậy, chúng ta có thể đơn giản hóa lệnh để:

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

ở đây chúng tôi sử dụng match việc bắt các nhóm và đưa chúng vào danh sách biến ary. $0là toàn bộ dòng. ary[0]là tất cả mọi thứ phù hợp. ary[1...n]là mỗi nhóm.

perl

Chúng tôi cũng có thể làm điều đó với perl:

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

Các kết quả:

> 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

làm thế nào để lấy tập tin ví dụ:

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

Với GNU coreutils, bạn có thể sử dụng csplitđể chia một tệp thành các phần được phân tách bằng regrec, bằng regex như được hiển thị bởi geekizard .

Đây là một kịch bản awk di động để chia một tập tin thành từng mảnh. Nó hoạt động bởi

  • gọi getline để đối phó với dải phân cách đa dòng (2 dòng);
  • đặt một biến outfilethành tên của tệp để in, khi gặp tiêu đề phần.
BEGIN {outfile="header.txt"}
{
    while (/^[0-9]+\.$/) {
        prev = $0; getline;
        if ($0 == "") outfile = prev "txt";
        print prev >outfile
    }
    print >outfile
}

Điều này hoạt động trên nguyên tắc , nhưng tiêu đề phần của dữ liệu trang web thực tế không được biểu thị bằng biểu thức chính quy (tương tự như vậy với câu trả lời của geekizard). Hàng đầu nunber. được theo sau bởi văn bản có chứa dấu gạch chéo /. Tôi khá chắc chắn rằng two newlines ixtmixilix được đề cập là 2 dòng trống đi trước định danh số và sẽ xác định cụ thể hơn tiêu đề, nhưng vì dữ liệu trên trang web chỉ khớp với /^[0-9]+\. các tiêu đề của phần, nên không cần phải phục vụ cho chúng ( Trong trường hợp cụ thể này). cảm ơn; đặc biệt là cho phần giới thiệu đến getline.. PS. trong khi có thể nếu?
Peter.O

@fred geekizard và tôi đã đi theo mô tả trong câu hỏi, không phải bởi dữ liệu trên trang web. Bố cục sẽ phụ thuộc vào công cụ kết xuất HTML được sử dụng để chuyển đổi thành văn bản; phần mà điều này được hiển thị từ một trang web thực sự không liên quan đến câu hỏi. | | whilecó trong trường hợp đầu vào chứa 1.\n2.\n\n(trong đó \nlà dòng mới): 2.phải được nhận ra trong dòng tiêu đề. Nó sẽ không xảy ra ở đây, nhưng tôi hỗ trợ nó trong mã của mình để làm cho nó chung chung hơn (và phù hợp với đặc điểm kỹ thuật trong câu hỏi hơn).
Gilles 'SO- ngừng trở nên xấu xa'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.