2010-05-13 35 views
6

Tengo un archivo XML gigantesco (4GB) que actualmente estoy dividiendo en fragmentos con la función de "división" de Linux (cada 25,000 líneas, no por bytes). Esto generalmente funciona muy bien (termino con unos 50 archivos), excepto que algunas de las descripciones de datos tienen saltos de línea, y con tanta frecuencia los archivos de fragmentos no tienen las etiquetas de cierre correctas, y mi analizador se ahoga a mitad del proceso.¿Cómo usar linux csplit para cortar archivos XML masivos?

Ejemplo archivo: (nota: normalmente cada una "lista" nodo XML se supone que es en su propia línea)

<?xml version="1.0" encoding="UTF-8"?> 
<listings> 
<listing><date>2009-09-22</date><desc>This is a description WITHOUT line breaks and works fine with split</desc><more_tags>stuff</more_tags></listing> 
<listing><date>2009-09-22</date><desc>This is a really 
annoying description field 
WITH line breaks 
that screw the split function</desc><more_tags>stuff</more_tags></listing> 
</listings> 

Entonces a veces mi puntas abiertas arriba como

<?xml version="1.0" encoding="UTF-8"?> 
<listings> 
<listing><date>2009-09-22</date><desc>This is a description WITHOUT line breaks and works fine with split</desc><more_tags>stuff</more_tags></listing> 
<listing><date>2009-09-22</date><desc>This is a really 
annoying description field 
WITH line breaks ... 
EOF 

Así - He estado leyendo sobre "csplit" y parece que podría funcionar para resolver este problema. Me parece que no puede obtener la expresión regular correcta ...

Quiero básicamente la misma salida de archivos ~ 50ish

Algo así como:

*csplit -k myfile.xml '/</listing>/' 25000 {50} 

Cualquier ayuda sería grande Gracias!

Respuesta

5

No se puede obtener un archivo XML válido de esta manera. Le recomendaría que escriba un programa Java usando StaX, que, si utiliza la implementación de WoodStox, se pondrá en marcha de manera bastante rápida y dentro y fuera del XML.

+0

Gracias por la sugerencia, sin embargo, en mis trozos agrego en la primera línea y/o la última línea para cerrar la etiqueta principal * lisings "para que cada subarchivo sea válido. He estado haciendo esto por un tiempo y funciona bien agregar esas líneas después de th e dividido – Fred

+1

Eres familiar con Java, VTD-XML extendido te da la flexibilidad para dividir el documento usando XPath, mira el ejemplo a continuación http://snippets.dzone.com/posts/show/11269 –

1

Uso Perl:

perl -p -i -e 'unless(defined$fname){$fname="xx00";open$fh,">",$fname;}$size+=length;print$fh $_;if($size>%MAX% and [email protected]</listing>@){$fname++;$size=0;open$fh,">",$fname;}' 

Reemplazar %MAX% con un tamaño máximo de un archivo en bytes.

+0

¿Cómo es esto mejor que csplit? ? – bmargulies

+0

¿Funcionaría esto con las secciones de CDATA? En general, no creo que sea una buena idea intentar modificar xml con herramientas que no sean xml; entonces usar el analizador xml basado en Perl (etc) tendría más sentido IMO. Un poco más de trabajo, pero en realidad funcionaría. – StaxMan

1

Antes que nada, usa una barra dentro de la expresión regular. Para estar seguro, puede citarlo para que no se confunda con el delimitador final: /<\/listing>/.

Sin embargo, en este caso sería más conveniente dividir en la etiqueta de inicio en lugar de la etiqueta de cierre, ya que cada fragmento contiene hasta pero no incluye la línea coincidente. Por lo que podría intentar algo como esto:

csplit myfile.xml '/^<listing>/' '{*}' 

Se utiliza el ancla comienzo de línea ^ allí para asegurarse de que sólo se divide antes de las líneas donde aparece la etiqueta de inicio al comienzo de la línea.

4

Recomendaría no intentar usar expresiones regulares (o coincidencia de texto ingenua) para cualquier manipulación xml, incluida la división. XML es lo suficientemente complicado como para tratar con ese analizador debe ser utilizado; y debido a limitaciones de memoria, uno que puede hacer un análisis de "transmisión" (también conocido como incremental/fragmentado). Estoy muy familiarizado con Java, donde usaría el analizador Stax (o SAX) y el escritor/generador para hacer esto; la mayoría de los otros idiomas tienen algo similar. O si la entrada es lo suficientemente regular, herramienta de enlace de datos (JAXB) que puede enlazar subárboles.

Hacerlo de forma correcta puede requerir un poco más de trabajo, pero funcionaría, tratando las cosas que xml puede tener (por ejemplo, las secciones CDATA no se pueden dividir, las soluciones regexp invariablemente tienen casos que no manejarían, hasta que uno básicamente escrito un analizador xml completo).

0

Habiendo cumplido con el mismo requisito (dividir un gran archivo XML en el cierre de elementos secundarios de nivel superior pero en fragmentos), no creo que csplit pueda lograrlo si solo funciona como se describe en su página man.

Para poder hacer esto sería necesario ..

  1. La capacidad de los patrones de grupo y repetir un grupo, no sólo un único patrón
  2. La capacidad de tener un patrón que capturó pero no lo hizo escindir un nuevo archivo

que permitiría a un grupo como

tail bigfile.xml -n-1 | head -n+1 | csplit - '{ 25000 /<\/end>/ }' {*} 

No veo ninguna de estas características descritas en su página man (pero creo que serían adiciones útiles).

1

yo no usaría csplit, me gustaría utilizar el programa Perl xml_split en cambio, es muy bonito

$ ls -h . 
junk.xml 

$ cat junk.xml 
<?xml version="1.0" encoding="UTF-8"?> 
<listings> 
<listing><date>2009-09-22</date><desc>This is a description WITHOUT line breaks and works fine with split</desc><more_tags>stuff</more_tags></listing> 
<listing><date>2009-09-22</date><desc>This is a really 
annoying description field 
WITH line breaks 
that screw the split function</desc><more_tags>stuff</more_tags></listing> 
</listings> 

$ xml_split -s 20 junk.xml 

$ ls -h . 
junk-00.xml junk-01.xml junk-02.xml junk.xml 

$ cat junk-00.xml 

<listings> 
<?merge subdocs = 0 :junk-01.xml?> 
<?merge subdocs = 0 :junk-02.xml?> 
</listings> 

$ cat junk-02.xml 
<?xml version="1.0" encoding="UTF-8"?> 
<xml_split:root xmlns:xml_split="http://xmltwig.com/xml_split"> 
<listing><date>2009-09-22</date><desc>This is a really 
annoying description field 
WITH line breaks 
that screw the split function</desc><more_tags>stuff</more_tags></listing> 
</xml_split:root> 

Ok, por lo que la opción -s se divide en tamaño no número de líneas (elementos), pero https://metacpan.org/pod/distribution/XML-Twig/tools/xml_split/xml_split puede parchear fácilmente para dividir cada 25k subelementos

Cuestiones relacionadas