Обработка больших xml файлов (~ 300мб)

На самом деле это единственный раздел про unix на этом форуме

Модераторы: /dev/random, Модераторы разделов

Аватара пользователя
SLEDopit
Модератор
Сообщения: 4824
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Обработка больших xml файлов (~ 300мб)

Сообщение SLEDopit »

Имеется xml файл со структурой:

Код: Выделить всё

<shop id="138">
<куча параметров>значение</куча параметров>
                </shop>
<shop id="139">
<куча параметров>значение</куча параметров>
                </shop>
<shop id="145">
<куча параметров>значение</куча параметров>
                </shop>
Задача:
Разбить файл на кучу маленьких по принципу:
Первый файл:

Код: Выделить всё

<shop id="138">
<куча параметров>значение</куча параметров>
                </shop>
Второй файл:

Код: Выделить всё

<shop id="139">
<куча параметров>значение</куча параметров>
                </shop>
И т.д., где id номер - имя файла.
Я сделал это следующим образом:

Код: Выделить всё

#!/bin/bash
cat -n s.xml | grep -E "<shop|shop>" | sed -r "s/<y[^>]*>//;s/[\t\n]//g;s=<s=,<=;N;s/\n//g;s/^/sed -n '/;s/\t/p;' s.xml/;s/(id=)([^>]*[0-9])(.*xml)/\1\2\3 | LANG=ru_RU.UTF-8 enconv > \2.xml/;s/<[^>]*>//g;s/\"//;s/,([0-9]*)p;/,\1p;\1q;/" > tmp.sh
chmod +x tmp.sh
./tmp.sh
rm tmp.sh
Скрипт генерирует другой скрипт вида:

Код: Выделить всё

sed -n '3170118,3182227p;3182227q;' s.xml | LANG=ru_RU.UTF-8 enconv > 14137.xml

По моему весьма нерационально так прогонять sed по файлу много раз. Время работы скрипта ~ 30-40 минут.
Но ничего умнее я не придумал.
UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали:
Аватара пользователя
deadhead
Сообщения: 1913
Статус: zzz..z

Re: Обработка больших xml файлов (~ 300мб)

Сообщение deadhead »

/оффтопик не знаю с какой целью вам понадобилось разбивать файл... если это связано с трудностями обработки столь большого объема данных, то может глянуть в сторону SAX
[x] close
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Обработка больших xml файлов (~ 300мб)

Сообщение drBatty »



Код: Выделить всё

#!/bin/sed -rf
/<shop id="[0-9]+">/{
h
# выделение номера
s/.*<shop id="([0-9]+)">.*/\1/
x
:l
\~</shop>~ b el
G
s/(.*)\n(.*)/echo '\1' >> \2.xml/e
n
b l

:el
G
s/(.*)\n(.*)/echo '\1' >> \2.xml/e
}

лениво отлаживать, но думаю идея ясна.
я не рассмотрел случай, если файл обрывается посредине тега, и ещё в файле не должно быть прямых кавычек (это можно исправить)
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
SLEDopit
Модератор
Сообщения: 4824
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Re: Обработка больших xml файлов (~ 300мб)

Сообщение SLEDopit »

drBatty
да, спасибо большое.
UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали:
Аватара пользователя
SLEDopit
Модератор
Сообщения: 4824
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Re: Обработка больших xml файлов (~ 300мб)

Сообщение SLEDopit »

даа, все таки мой способ в разы быстрее.

Код: Выделить всё

 $ ./drBatty.sed s.xml
real    166m38.887s
user    72m24.376s
sys     84m7.235s

$ ./SLEDopit.sh
real    33m12.797s
user    31m39.507s
sys     0m41.899s

$ ls -lh s.xml
-rw-rw-r-- 1 user user 226M Mar 19 12:08 s.xml
пошел думать дальше.
UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Обработка больших xml файлов (~ 300мб)

Сообщение drBatty »

SLEDopit писал(а):
19.03.2010 19:31
даа, все таки мой способ в разы быстрее.

без файла сложно сказать в чём беда.
SLEDopit писал(а):
19.03.2010 11:22
<куча параметров>значение</куча параметров>

наверное, тут слишком много букв.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
SLEDopit
Модератор
Сообщения: 4824
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Re: Обработка больших xml файлов (~ 300мб)

Сообщение SLEDopit »

drBatty писал(а):
19.03.2010 19:51
наверное, тут слишком много букв.
да, там может быть около миллиона строк в каждой секции <shop> ..</shop>, а то и больше.
UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Обработка больших xml файлов (~ 300мб)

Сообщение drBatty »

SLEDopit писал(а):
19.03.2010 20:07
да, там может быть около миллиона строк в каждой секции, а то и больше.

ага. и везде sed ищет </shop>, а если не нашла, начинает пересылать это всё по pipe в echo...
egrep тут быстрее конечно, особенно (ЕМНИП) потому, что она не UTF.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
ZyX
Сообщения: 355
ОС: Gentoo

Re: Обработка больших xml файлов (~ 300мб)

Сообщение ZyX »

Если XML валидный, то можно использовать следующий скрипт:

Код: Выделить всё

#!/usr/bin/perl
use strict;
use warnings;
use XML::LibXML::Reader;

my $reader=XML::LibXML::Reader->new(FD=>fileno(STDIN));
while($reader->nextElement("shop")==1)
{
    my $str=$reader->readOuterXml();
    my $F;
    open($F, ">", (($reader->getAttribute("id")).".xml"));
    print $F $str;
    close $F;
}

Пример работы:

Код: Выделить всё

(zyx:~/tmp/perl) % echo '<rootElement><shop id="1">...</shop><shop id="2">.._</shop></rootElement>' | ./test.pl
Use of uninitialized value in subroutine entry at /usr/lib/perl5/vendor_perl/5.8.8/i686-linux/XML/LibXML/Reader.pm line 153.
(zyx:~/tmp/perl) % la
итого 12K
-rw-r--r-- 1 zyx  23 Мар 25 05:07 1.xml
-rw-r--r-- 1 zyx  23 Мар 25 05:07 2.xml
-rwxr-xr-x 1 zyx 762 Мар 25 05:06 test.pl*
(zyx:~/tmp/perl) % cat 2.xml
<shop id="2">.._</shop>


Спасибо сказали:
Аватара пользователя
ZyX
Сообщения: 355
ОС: Gentoo

Re: Обработка больших xml файлов (~ 300мб)

Сообщение ZyX »

Если же
1. строка с <shop id="(\d+)" не содержит элементов предыдущего <shop>;
2. файл начинается с <shop id="(\d+)";
3. нет вложенных <shop id="(\d+)";
то всё можно уложить в одну строку:

Код: Выделить всё

(zyx:~/tmp/perl) % cat file | perl -p -i -e 'open(F, ">", ($1).".xml") if /<shop id="(\d+)"/; print F;'


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

PPS: Судя по скрипту из первого сообщения, на все сгенерированные файлы
с именами вида «id.xml» придётся отдельным скриптом кастовать enconv.
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: Обработка больших xml файлов (~ 300мб)

Сообщение eddy »

Да накатайте вы на сях, чего мучиться-то? Я, например, сначала сделал парсер логов Squid на баше, так он жутко долго считал. Переписал на сях:

Код: Выделить всё

time parsesquid -f 3

Провожу подсчет (от 28811794 до 39207733)

====================================================================
......... 10% ......... 20% ......... 30% ......... 40% ......... 50% ......... 60% ......... 70% ......... 80% ......... 90% ......... 100%
Получено информации
        из мира: 646020720 байт (616.09 МБ);
        из кэша: 33416064 байт (31.87 МБ)

====================================================================
0.19user 0.01system 0:00.28elapsed 74%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+190minor)pagefaults 0swaps

А вот так было на баше:

Код: Выделить всё

time parsesquid~~ "3/1/2010 00:00"
Preparing files...
ready
searching from date=1267390800 till now
Counting bytes from outside
total: 652276230 bytes (622.05 MB)

Counting bytes from cache
total: 685695579 bytes (653.93 MB)
13.39user 1.84system 0:20.03elapsed 76%CPU (0avgtext+0avgdata 0maxresident)k
288inputs+8304outputs (1major+5159minor)pagefaults 0swaps

Наглядно?

P.S. Кстати, заметил ошибку: bash-скрипт еще и неправильно считал данные из кэша.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
ZyX
Сообщения: 355
ОС: Gentoo

Re: Обработка больших xml файлов (~ 300мб)

Сообщение ZyX »

eddy писал(а):
24.03.2010 23:21
Да накатайте вы на сях, чего мучиться-то?

На perl такой скрипт написать быстрее. При достаточно нормальной разбивке на
строки проигрыш в скорости моего второго скрипта по сравнению с C’шным аналогом
будет незаметен на фоне длительности разработки.
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: Обработка больших xml файлов (~ 300мб)

Сообщение eddy »

ZyX писал(а):
24.03.2010 23:46
При достаточно нормальной разбивке на
строки проигрыш в скорости моего второго скрипта по сравнению с C’шным аналогом
будет незаметен на фоне длительности разработки.

Лучше день потерять, зато потом за час долететь ©
Мне это жена всегда припоминает, когда сижу два-три вечера, чтобы какую-нибудь удобную утилитку написать...
Кстати, если бы все люди следовали этой поговорке, у нас давно уже было бы высокоразвитое технократическое общество.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
SLEDopit
Модератор
Сообщения: 4824
Статус: фанат консоли (=
ОС: GNU/Debian, RHEL

Re: Обработка больших xml файлов (~ 300мб)

Сообщение SLEDopit »

ZyX писал(а):
24.03.2010 21:32
то всё можно уложить в одну строку:
впечатляет:

Код: Выделить всё

 $ time cat s.xml | perl -p -i -e 'open(F, ">", ($1).".xml") if /<shop id="(\d+)"/; print F;'

real    0m12.700s
user    0m5.756s
sys    0m3.472s
спасибо большое.
ZyX писал(а):
24.03.2010 21:32
с именами вида «id.xml» придётся отдельным скриптом кастовать enconv.
это не проблема. я просто изначальный файл прогоню через enconv.

UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали: