Как использовать Linux Csplit, чтобы измельчить массивный файл XML? - PullRequest
6 голосов
/ 14 мая 2010

У меня есть гигантский (4 ГБ) XML-файл, который я сейчас разбиваю на куски с помощью функции linux «split» (каждые 25 000 строк - не байтами). Обычно это прекрасно работает (у меня получается около 50 файлов), за исключением того, что некоторые описания данных имеют разрывы строк, и поэтому часто файлы чанков не имеют надлежащих закрывающих тегов - и мой анализатор прерывается на полпути в процессе обработки.

Файл примера: (примечание: обычно каждый 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 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

Итак - я читал о "csplit", и похоже, что он может решить эту проблему. Я не могу понять правильное выражение ...

В основном, я хочу такой же вывод для ~ 50-ых файлов

Что-то вроде:

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

Любая помощь будет отличной Спасибо!

Ответы [ 6 ]

5 голосов
/ 14 мая 2010

Вы не можете получить действительный файл XML таким способом. Я бы порекомендовал вам написать Java-программу с использованием StaX, которая, если вы используете реализацию WoodStox, будет очень быстро передавать и выводить XML.

4 голосов
/ 10 июля 2010

Я бы рекомендовал не пытаться использовать регулярные выражения (или простое сопоставление текста) для любых манипуляций с XML, включая разбиение. XML достаточно сложен, чтобы иметь дело с этим парсером; и из-за ограничений памяти, тот, который может выполнять «потоковый» (иначе говоря, инкрементальный / чанкированный) анализ. Я больше всего знаком с Java, где вы могли бы использовать Stax (или SAX) парсер и писатель / генератор для этого; большинство других языков имеют нечто подобное. Или, если ввод достаточно регулярный, инструмент привязки данных (JAXB), который может связывать поддеревья.

Правильное выполнение может быть немного больше работы, но на самом деле будет работать, имея дело с вещами, которые может иметь xml (например, разделы CDATA не могут быть разделены; решения regexp неизменно имеют случаи, которые они не будут обрабатывать, пока в основном написал полный xml-парсер).

1 голос
/ 24 февраля 2011

Я бы не использовал csplit, я бы использовал вместо этого программу perl xml_split, это очень приятно

$ 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>

Хорошо, поэтому опция -s делится на размер не по количеству строк (элементов), а по размеру. https://metacpan.org/pod/distribution/XML-Twig/tools/xml_split/xml_split может быть легко исправлен для разделения на каждые 25 тыс. субэлементов

1 голос
/ 15 мая 2010

Прежде всего, вы используете косую черту внутри регулярного выражения. Чтобы быть в безопасности, вы можете заключить его в кавычки, чтобы его не спутали с конечным разделителем: /<\/listing>/.

Однако в этом случае было бы удобнее разделять начальный тег, а не конечный, поскольку каждый блок содержит до , но не включает совпадающую строку. Так что вы можете попробовать что-то вроде этого:

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

Использовал привязку начала строки ^, чтобы убедиться, что она разбивается только перед строками, где начальный тег появляется в начале строки.

1 голос
/ 14 мая 2010

Используйте perl:

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

Заменить %MAX% на максимальный размер одного файла в байтах.

0 голосов
/ 19 октября 2010

Имея то же самое требование (разделить большой XML-файл на закрытие дочерних элементов верхнего уровня, но на порции), я не думаю, что csplit сможет достичь этого, если будет работать только так, как описано на его странице руководства. 1001 *

Чтобы сделать это, нужно ..

  1. Возможность группировать шаблоны и повторять группу, а не просто один шаблон
  2. Возможность иметь шаблон, который захватил, но не отделил новый файл

Это включило бы группу наподобие

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

Я не вижу ни одной из этих функций, описанных на его странице руководства (но думаю, что они будут полезными дополнениями).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...