Divisão de arquivos de texto com base em uma expressão regular

16

Eu tenho um arquivo de texto que quero dividir em 64 partes desiguais, de acordo com os 64 hexagramas do Yi Jing. Como a passagem para cada hexagrama começa com alguns dígitos, um ponto e duas novas linhas, a regex deve ser bem fácil de escrever.

Mas como eu realmente divido o arquivo de texto em 64 novos arquivos de acordo com este regex? Parece mais uma tarefa para perl. Mas talvez exista uma maneira mais óbvia de que estou totalmente ausente.

ixtmixilix
fonte

Respostas:

23

Isso seria csplitexceto que o regex precisa ser uma única linha. Isso também seddificulta; Eu iria com Perl ou Python.

Você pode ver se

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

é bom o suficiente para seus propósitos. ( csplitrequer um POSIX BRE, portanto ele não pode ser usado \dou +, entre outros.)

geekosaur
fonte
Obrigado, @geekosaur. Funcionou perfeitamente, embora eu tenha que mudar para {63}.
Ixtmixilix
1
Então, '\.'não vai funcionar também?
Vanuan 03/02
4

Eu acho que o melhor caminho é awke gawk.

awk

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

-Firá especificar os campos separados para cada linha. É uma regex, aqui usamos vários separadores: ". "e " / ". Assim, uma linha como 1. Ch'ien / The Creativeserá dividida em 3 campos: 1 Ch'iene The Creative. Mais tarde, podemos nos referir a esses campos com $n. $0é a linha inteira.

Em seguida, dizemos ao awk para combinar as linhas com o padrão. ^[0-9]{1,3}[.]Se houver correspondência, atribuímos valor a x. O valor x será usado como nome de arquivo para printoperação. Neste exemplo, usamos "F"$1"("$2").txt"para que a linha 1. Ch'ien / The Creativedê um nome de arquivoF1(Ch'ien).txt

gawk

No gawk, também podemos acessar o grupo capturado. Assim, podemos simplificar o comando para:

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

aqui usamos matcha captura dos grupos e os colocamos na lista de variáveis ary. $0é a linha inteira. ary[0]é tudo combinado. ary[1...n]é cada grupo.

perl

Também podemos fazer isso com perl:

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

Resultados:

> 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

como obter o arquivo de exemplo:

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

Com o GNU coreutils, você pode usar csplitpara dividir um arquivo em partes delimitadas por regexp, como mostra o geekosaur .

Aqui está um script awk portátil para dividir um arquivo em pedaços. Funciona por

  • chamando getlinepara lidar com o separador de múltiplas linhas (2 linhas);
  • definindo uma variável outfilepara o nome do arquivo para imprimir, quando um cabeçalho da seção for encontrado.
BEGIN {outfile="header.txt"}
{
    while (/^[0-9]+\.$/) {
        prev = $0; getline;
        if ($0 == "") outfile = prev "txt";
        print prev >outfile
    }
    print >outfile
}
Gilles 'SO- parar de ser mau'
fonte
Isso funciona em princípio , mas o cabeçalho da seção dos dados reais da página da web não é tão representado pelo regex (da mesma forma que com a resposta do geekosaur). O início nunber. é seguido pelo texto que contém uma barra /. Tenho certeza de que o two newlines ixtmixilix mencionado são as duas linhas em branco que precedem o identificador numérico e identificariam mais especificamente o cabeçalho, mas como os dados na página da Web correspondem apenas /^[0-9]+\. aos cabeçalhos das seções, não é necessário atendê-los ( neste caso em particular). obrigado; especialmente para a introdução a getline.. PS. enquanto pode ser se?
Peter.O
@ geekosaur fred e eu fui pela descrição na pergunta, não pelos dados no site. O layout dependerá do mecanismo de renderização HTML usado para converter em texto; a parte em que isso é renderizado a partir de uma página da web é realmente irrelevante para a pergunta. ||| whileexiste no caso de a entrada conter 1.\n2.\n\n(onde \nestão as novas linhas): elas 2.devem ser reconhecidas na linha do cabeçalho. Isso não ocorrerá aqui, mas eu o apóio no meu código para torná-lo mais geral (e corresponder mais estritamente à especificação da pergunta).
Gilles 'SO- stop be evil'