2011-02-10 21 views
11

Hola chicos, espero que el tema sea lo suficientemente claro, no he encontrado nada específicamente sobre esto en el contenedor previamente solicitado. Intenté implementar esto en Perl o Python, pero creo que podría estar intentando demasiado.Dividir un archivo de texto grande en 200 archivos txt más pequeños en una expresión regular mediante script de shell en BASH

¿Hay un simple comando shell/oleoducto que dividir mi archivo .txt 4 MB en archivos .txt independiente, basado en un principio y el final de expresiones regulares?

Proporciono una pequeña muestra del archivo a continuación ... para que pueda ver que cada "historia" comienza con la frase "X de XXX DOCUMENTOS", que podría utilizarse para dividir el archivo.

creo que esto debe ser fácil y me sorprendería si bash no puede hacerlo - más rápido que Perl/Py.

aquí está:

      1 of 999 DOCUMENTS 


       Copyright 2011 Virginian-Pilot Companies LLC 
          All Rights Reserved 
        The Virginian-Pilot(Norfolk, VA.) 

... 



          3 of 999 DOCUMENTS 


        Copyright 2011 Canwest News Service 
          All Rights Reserved 
          Canwest News Service 

... 

Gracias de antemano por toda su ayuda.

Ross

+1

¿es necesario mucho texto de ejemplo? – jakev

+1

Edita y elimina el 95% del texto en tu pregunta. –

+0

posible duplicado de [un archivo Dividir en múltiples archivos basado en delimitador] (http://stackoverflow.com/questions/11313852/split-one-file-into-multiple-files-based-on-delimiter) – tripleee

Respuesta

22
awk '/[0-9]+ of [0-9]+ DOCUMENTS/{g++} { print $0 > g".txt"}' file 

usuarios de OSX necesitarán gawk, como la orden interna awk producirá un error como awk: illegal statement at source line 1

Rubí (1.9+)

#!/usr/bin/env ruby 
g=1 
f=File.open(g.to_s + ".txt","w") 
open("file").each do |line| 
    if line[/\d+ of \d+ DOCUMENTS/] 
    f.close 
    g+=1 
    f=File.open(g.to_s + ".txt","w") 
    end 
    f.print line 
end 
+0

OH y tenemos un ganador .... velocidad * Y * elegancia Pasé un verano muy húmedo en 1997 con el libro O'Reilly sed/awk. Ojalá pudiera recordar todo eso ahora. Yo * iré * y lo conseguiré tmrw. ** GRACIAS ** – rosser

+1

Esta solución pone la línea correspondiente en el nuevo archivo, que responde a la pregunta. Pero si, como yo, quiere poner la línea coincidente en el archivo anterior antes de comenzar el nuevo, haría esto: 'awk '{print $ 0> n" .txt "}/text to match/{n ++} ' – indiv

+1

Nota: en Mac OS X necesita' gawk' de por ejemplo MacPorts para que esto funcione –

0

expresiones regulares para que coincida con "X DOCUMENTOS XXX" es
\ d {1,3} de \ d {1,3) DOCUMENTOS

línea de lectura por línea y empezar a escribir nuevo archivo sobre la coincidencia de expresiones regulares debería estar bien.

-1

No comprobado:

base=outputfile 
start=1 
pattern='^[[:blank:]]*[[:digit:]]+ OF [[:digit:]]+ DOCUMENTS[[:blank:]]*$ 

while read -r line 
do 
    if [[ $line =~ $pattern ]] 
    then 
     ((start++)) 
     printf -v filecount '%4d' $start 
     >"$base$filecount" # create an empty file named like foo0001 
    fi 
    echo "$line" >> "$base$filecount" 
done 
+0

Por cierto , lo anterior es puro Bash. Además, estoy seguro de que Python o Perl serían mucho más rápidos. –

+1

¿Puedes hacerlo con csplit? csplit -k -z --digits = 3 --suffix = '% d.TXT' --prefix = ARCHIVO *.TXT/'SPLITONTHIS' – rosser

+0

@rosser - este es un candidato para split, no sé csplit aunque – sln

1

¿Qué tan difícil ¿trató en Perl?

Editar Aquí hay un método más rápido. Divide el archivo y luego imprime los archivos de la parte.

use strict; 
use warnings; 

my $count = 1; 

open (my $file, '<', 'source.txt') or die "Can't open source.txt: $!"; 

for (split /(?=^.*\d+[^\S\n]*of[^\S\n]*\d+[^\S\n]*DOCUMENTS)/m, join('',<$file>)) 
{ 
    if (s/^.*(\d+)\s*of\s*\d+\s*DOCUMENTS.*(\n|$)//m) 
    { 
     open (my $part, '>', "Part$1_$count.txt") 
      or die "Can't open Part$1_$count for output: $!"; 
     print $part $_; 
     close ($part); 
     $count++; 
    } 
} 
close ($file); 

Esta es la línea por el método de línea:

use strict; 
use warnings; 

open (my $masterfile, '<', 'yourfilename.txt') or die "Can't open yourfilename.txt: $!"; 

my $count = 1; 
my $fh; 

while (<$masterfile>) { 
    if (/(?<!\d)(\d+)\s*of\s*\d+\s*DOCUMENTS/) { 
     defined $fh and close ($fh); 
     open ($fh, '>', "Part$1_$count.txt") or die "Can't open Part$1_$count for output: $!"; 
     $count++; 
     next; 
    } 
    defined $fh and print $fh $_; 
} 
defined $fh and close ($fh); 
close ($masterfile); 
+0

'$ count' no está definido. Sospecho que querías decir '$ cnt'. Además, la primera vez que ejecuta el ciclo '$ fh' no está definido, por lo que obtendrá un error/advertencia' No se puede usar un valor indefinido como referencia de símbolo' cuando intente cerrar '$ fh'. – CanSpice

+0

@CanSpice, ¿cómo está eso ahora? – sln

+1

¡Se ve mejor ahora! – CanSpice

9

Como se sugiere en otras soluciones, se puede usar para ese csplit:

csplit csplit.test '/^\.\.\./' '{*}' && sed -i '/^\.\.\./d' xx* 

no he encontrado una mejor manera de deshacerse del separador que recuerda en los archivos divididos.

+0

No puedo intentarlo ahora porque en Windows, pero la página man de csplit parece sugerir usar% REGEX% en lugar de/REGEX/para eso: /REGEXP/[OFFSET] copiar pero no incluir una línea coincidente % REGEXP% [OFFSET] saltar a, pero sin incluir una línea coincidente – Spikolynn

Cuestiones relacionadas