Есть большой XML файл. Порядка полугига. Его надо преобразовать в другой формат.
Есть ли готовые решения, которые не пытаются загрузить файл в память?
Нужно что-то чтобы читало его последовательно.
This software required Windows 95 or better...
So I installed Linux
Здравствуйте, hellas, Вы писали:
H>Есть большой XML файл. Порядка полугига. Его надо преобразовать в другой формат.
... H>Нужно что-то чтобы читало его последовательно.
SAX парсер тебе в руки.
Точно идёт в составе msxml.
expat тоже SAX поддерживает.
Здравствуйте, hellas, Вы писали:
H>Есть большой XML файл. Порядка полугига. Его надо преобразовать в другой формат. H>Есть ли готовые решения, которые не пытаются загрузить файл в память? H>Нужно что-то чтобы читало его последовательно.
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, hellas, Вы писали:
H>>Есть большой XML файл. Порядка полугига. Его надо преобразовать в другой формат. H>>Есть ли готовые решения, которые не пытаются загрузить файл в память? H>>Нужно что-то чтобы читало его последовательно.
CS>Вот это http://www.codeproject.com/cpp/HTML_XML_Scanner.asp я делал как раз для таких целей. CS>Это т.н. push parser — теоретически это самый быстрый способ потоковой обработки XML.
А можно пример обработки файла?
This software required Windows 95 or better...
So I installed Linux
Здравствуйте, hellas, Вы писали:
CS>>Вот это http://www.codeproject.com/cpp/HTML_XML_Scanner.asp я делал как раз для таких целей. CS>>Это т.н. push parser — теоретически это самый быстрый способ потоковой обработки XML.
H>А можно пример обработки файла?
Если я правильно понял вопрос...
Вот самый наипростейший файловый входной stream для сканера
struct file_istream: public markup::instream
{
FILE *f;
file_istream(const char* src): f(0) { f = fopen(...); }
virtual wchar_t get_char() { int t = fgetc(f); return (t == EOF)? 0 : t; }
};
Если в XML используется encoding отличный от ascii то
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, hellas, Вы писали:
H>>Есть большой XML файл. Порядка полугига. Его надо преобразовать в другой формат. H>>Есть ли готовые решения, которые не пытаются загрузить файл в память? H>>Нужно что-то чтобы читало его последовательно.
CS>Вот это http://www.codeproject.com/cpp/HTML_XML_Scanner.asp я делал как раз для таких целей. CS>Это т.н. push parser — теоретически это самый быстрый способ потоковой обработки XML.
Помоему это называется pull parser, нет?
Re: Большой XML файл.
От:
Аноним
Дата:
29.10.06 21:22
Оценка:
Здравствуйте, hellas, Вы писали:
H>Есть большой XML файл. Порядка полугига. Его надо преобразовать в другой формат. H>Есть ли готовые решения, которые не пытаются загрузить файл в память?
Лучше всего воспользоватся стандартными решениями. А стандартные решения это SAX парсеры и expat.
В MSXML 3 (вроде, может и раньше) и выше входит СОМ-базированный SAX парсер. xerces-c- чистый С++. Скорость великолепная у обоих — гарантирую, что парсер боттлнеком не станет. Думаю что на "пропарсить полгигабайта" уйдёт 30-60 сек (на типичном компьютере) — на обработку аутпута (пихать в БД?) времени наверняка уйдёт значительно больше. expat — произведение исскусства, но может показатся чересчур низкоуровневым.
С самоделками лучше не связыватся — ХМЛ парсер оттестировать — всем миром надо повозится, всякие энкодинги, всевозможные конструкции — даже в упомянутых парсерах кой-какие проблемы встречаются и кое-что не иплементировано. Кроме того, SAX для валидации инпута может пользоватся схемой.
Re[2]: Большой XML файл.
От:
Аноним
Дата:
29.10.06 21:52
Оценка:
Кстати, при написании SAX-клиента — удобно пользоватся стеком (в смысле структуры данных) — Sax start element -> stack push, Sax end element -> stack pop.
У меня даже есть генерический фреймворк для написания такого кода (задача легко генерализутся и позволяет большую часть имплементировать раз и навсегда). Выложил бы в опен соурс, но неудобно — код не "отточен", документации нет, кроме автора никто не разберётся, нынешний вариант работает только с майкрософтским SAX-ом (легко исправить). А на приведение в порядок времени, естественно, нет.
Здравствуйте, korzhik, Вы писали:
K>>К сожалению я не видел C++ версии для промышленного использования.
Есть ещё штука которая называется xmlbooster — генерирует XML парсеры. Для шарпа — даже бесплатно, для других языков — за деньги. В какую сторону это потом работает — не знаю, может и pull.
Leif — упоминаю для порядка из-за его несуразной дороговизны и runtime fee за каждый deployment (про fee — отдельный договор, за сколько-то (1500?) долларов уплаченных за лицензию вы можете только девелопить 1 год) — в нём раньше был XML Reader вроде нетовского. Потом он вроде исчезал — парсилось как в DOM-e, сразу в память и никаких вариантов — может обратно появился. Парсеры генерируемые им — С++ классы, репрезентирующие XML элементы. Какие-то странности — вроде как отдельный элемент из представления распарсенного файла вы можете получить только по значению (если в нем 100 000 и 100 МБ данных — немного неэффективно). Страшенные имена — читать никак. Часть парсера — в closed source DLL.
Мне раз пришлось заменять Leif на xerces-c DOM — код стал меньше (!), проще, легче и быстрее(!).
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, korzhik, Вы писали:
K>>>К сожалению я не видел C++ версии для промышленного использования.
А>Есть ещё штука которая называется xmlbooster — генерирует XML парсеры. Для шарпа — даже бесплатно, для других языков — за деньги. В какую сторону это потом работает — не знаю, может и pull.
Интересная штука, надо будет потестировать потом на наших задачах.
Здравствуйте, c-smile, Вы писали:
CS>Вот это http://www.codeproject.com/cpp/HTML_XML_Scanner.asp я делал как раз для таких целей. CS>Это т.н. push parser — теоретически это самый быстрый способ потоковой обработки XML.
А в чём причина того, что pull парсер быстрее push? В чём фишка?
В чём прикол организовывать цикл в пользовательском коде, в не в коде библиотеки?
Здравствуйте, remark, Вы писали:
R>Здравствуйте, c-smile, Вы писали:
CS>>Вот это http://www.codeproject.com/cpp/HTML_XML_Scanner.asp я делал как раз для таких целей. CS>>Это т.н. push parser — теоретически это самый быстрый способ потоковой обработки XML.
R>А в чём причина того, что pull парсер быстрее push? В чём фишка? R>В чём прикол организовывать цикл в пользовательском коде, в не в коде библиотеки?
Более естественный на мой взгляд интерфейс для работы с XML.
В принципе если прогнать SAX парсер и pull парсер впустую на каком нибудь большом файле, то разница будет мизерная, если вообще будет.
Выигрыш даётся засчёт более удобного интерфейса.
В моём приложении я использую expat, но было дело, тестировал c-smile pull parser.
Тестировал на 30Мб XML файле.
С expat парсинг и построение dom 3 сек
С pull почти 2 сек
То есть выигрыш приближался к 30%
Но, ещё раз замечу, это не сравнение скорости парсеров, а сравнение различных подходов, то есть для тестирования pull парсера пришлось менять всю обработку данных.
И в итоге получить выигрыш за счёт болле удобного интерфейса
Здравствуйте, korzhik, Вы писали:
K>Здравствуйте, remark, Вы писали:
R>>Здравствуйте, c-smile, Вы писали:
CS>>>Вот это http://www.codeproject.com/cpp/HTML_XML_Scanner.asp я делал как раз для таких целей. CS>>>Это т.н. push parser — теоретически это самый быстрый способ потоковой обработки XML.
R>>А в чём причина того, что pull парсер быстрее push? В чём фишка? R>>В чём прикол организовывать цикл в пользовательском коде, в не в коде библиотеки?
K>Более естественный на мой взгляд интерфейс для работы с XML. K>В принципе если прогнать SAX парсер и pull парсер впустую на каком нибудь большом файле, то разница будет мизерная, если вообще будет.
K>Выигрыш даётся засчёт более удобного интерфейса.
K>В моём приложении я использую expat, но было дело, тестировал c-smile pull parser. K>Тестировал на 30Мб XML файле.
K>С expat парсинг и построение dom 3 сек K>С pull почти 2 сек
K>То есть выигрыш приближался к 30%
K>Но, ещё раз замечу, это не сравнение скорости парсеров, а сравнение различных подходов, то есть для тестирования pull парсера пришлось менять всю обработку данных. K>И в итоге получить выигрыш за счёт болле удобного интерфейса
Но всё-таки суть в чём?
Удобный интерфейс не тянет на первопричину повышения производительности
Сходу приходит в голову следующее: pull-парсер за счёт ручного "управления" процессом позволяет делать меньше лишней работы. Т.е. например, не создавать объект с аттрибутами (копировать строку со значением аттрибута), если пользовательский код не будет анализировать и считывать аттрибуты. Т.о. часть лишней работы подавляется.
А в остальном, я пока не вижу какого принципиального отличия между моделями, которые могли бы как-то влиять на производительность.
Хотя вот сейчас я подумал, что Push-парсер тоже может не делать никакой лишней работы, пока пользователь не запросит. Например, если пользователь не обращается к какому-то аттрибуту, то его значение может не копироваться. Т.е. вся реальная работа только по запросу пользователя. Хотя наверное так не делают (точнее просто не сделано) в популярных парсерах.
Здравствуйте, remark, Вы писали:
R>Удобный интерфейс не тянет на первопричину повышения производительности
а почему нет
R>Сходу приходит в голову следующее: pull-парсер за счёт ручного "управления" процессом позволяет делать меньше лишней работы. Т.е. например, не создавать объект с аттрибутами (копировать строку со значением аттрибута), если пользовательский код не будет анализировать и считывать аттрибуты. Т.о. часть лишней работы подавляется. R>А в остальном, я пока не вижу какого принципиального отличия между моделями, которые могли бы как-то влиять на производительность.
и это тоже
R>Хотя вот сейчас я подумал, что Push-парсер тоже может не делать никакой лишней работы, пока пользователь не запросит. Например, если пользователь не обращается к какому-то аттрибуту, то его значение может не копироваться. Т.е. вся реальная работа только по запросу пользователя. Хотя наверное так не делают (точнее просто не сделано) в популярных парсерах.
Здравствуйте, korzhik, Вы писали:
R>>А в чём причина того, что pull парсер быстрее push? В чём фишка? R>>В чём прикол организовывать цикл в пользовательском коде, в не в коде библиотеки?
А вот тут тоже можно, пожалуйста, поподробнее.
Вот что я пока вижу.
1. Необходимость организовывать цикл вручную, вместо цикла один раз написанного и внесённого в библиотеку. Имхо это минус.
2. С полпинка можно попытаться, например, получить значение аттрибута у парсера, когда на самом деле парсим не аттрибут, а элемент. Имхо большой минус. В push модели отлов таких ошибок будет на стадии компиляции. Т.е. если мы попали в калбек elementStart(const String& elementName). Значит ни к каким аттрибутум доступ мы просто не получим.
Это относится по крайней мере к тому интерфейсы парсера, который я вижу сейчас.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, korzhik, Вы писали:
R>>>А в чём причина того, что pull парсер быстрее push? В чём фишка? R>>>В чём прикол организовывать цикл в пользовательском коде, в не в коде библиотеки?
R>А вот тут тоже можно, пожалуйста, поподробнее. R>Вот что я пока вижу. R>1. Необходимость организовывать цикл вручную, вместо цикла один раз написанного и внесённого в библиотеку. Имхо это минус. R>2. С полпинка можно попытаться, например, получить значение аттрибута у парсера, когда на самом деле парсим не аттрибут, а элемент. Имхо большой минус. В push модели отлов таких ошибок будет на стадии компиляции. Т.е. если мы попали в калбек elementStart(const String& elementName). Значит ни к каким аттрибутум доступ мы просто не получим. R>Это относится по крайней мере к тому интерфейсы парсера, который я вижу сейчас.
такие ошибки возможны, согласен.
pull парсер средство более низкоуровневое чем SAX. И наверняка SAX парсеры строятся на основе pull парсера. Более низкоуровневое средство требует более высокой дисциплины
Я, к сожалению не могу сейчас аргументированно доказать что вот прям pull парсинг крутая вещь.
Но на своей задаче я пробовал pull парсинг, это сделало код обработки проще и быстрее, вот всё что я могку сказать.
Наверно чтобы полностью разобраться, надо поставить какую то небольшую задачу и решить её с помощью двух подходов: SAX и pull
Re[4]: Большой XML файл.
От:
Аноним
Дата:
01.11.06 08:52
Оценка:
Здравствуйте, korzhik, Вы писали:
K>С expat парсинг и построение dom 3 сек K>С pull почти 2 сек
K>То есть выигрыш приближался к 30%
Мне такое сравнение кажется несовсем корректным: expat способен обрабатывать, мягко говоря, несколько больше ситуаций, чем упомянутый парсер. Доведите функционал этого парсера до того, что есть в expat-е (а меньше — это ещё не настоящий парсер) — и еще не известно, кто будет быстрее. Полноценный ХМЛ парсинг — больше чем разбор строки на элементы и аттрибуты, выудить же элемент или что-то в этом роде из файла (с известым заранее энкодингом) — можно и используя регулярные выражения, например — может и ещё быстрее получится. По моему вы разные вещи сравниваете.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, korzhik, Вы писали:
K>>С expat парсинг и построение dom 3 сек K>>С pull почти 2 сек
K>>То есть выигрыш приближался к 30%
А>Мне такое сравнение кажется несовсем корректным: expat способен обрабатывать, мягко говоря, несколько больше ситуаций, чем упомянутый парсер. Доведите функционал этого парсера до того, что есть в expat-е (а меньше — это ещё не настоящий парсер) — и еще не известно, кто будет быстрее. Полноценный ХМЛ парсинг — больше чем разбор строки на элементы и аттрибуты, выудить же элемент или что-то в этом роде из файла (с известым заранее энкодингом) — можно и используя регулярные выражения, например — может и ещё быстрее получится. По моему вы разные вещи сравниваете.
А можно и просто поискать в строке "<element_name>" и "</element_name>" — будет ещё быстрее. Гораздо!
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, korzhik, Вы писали:
K>>С expat парсинг и построение dom 3 сек K>>С pull почти 2 сек
K>>То есть выигрыш приближался к 30%
А>Мне такое сравнение кажется несовсем корректным: expat способен обрабатывать, мягко говоря, несколько больше ситуаций, чем упомянутый парсер. Доведите функционал этого парсера до того, что есть в expat-е (а меньше — это ещё не настоящий парсер) — и еще не известно, кто будет быстрее. Полноценный ХМЛ парсинг — больше чем разбор строки на элементы и аттрибуты, выудить же элемент или что-то в этом роде из файла (с известым заранее энкодингом) — можно и используя регулярные выражения, например — может и ещё быстрее получится. По моему вы разные вещи сравниваете.
я понял о чём вы.
c-smile pull parser не работает с namespace и entity но в тестируем файле этого и не было.
Результатом тестирования стало две программы: одна использовала expat другая c-smile pull parser
Функциональность у них была одинаковая. Программа с pull парсингом работала быстрее.
Чтож здесь некорректного?
Re[6]: Большой XML файл.
От:
Аноним
Дата:
01.11.06 09:12
Оценка:
Здравствуйте, korzhik, Вы писали:
K>pull парсер средство более низкоуровневое чем SAX. И наверняка SAX парсеры строятся на основе pull парсера. Более низкоуровневое средство требует более высокой дисциплины
Не соглашусь. pull-парсером пользоватся проще. В .НЕТ ведь нету SAX-а? Есть pull XML Reader. У майкрософта читал, что pull как раз потому, что писать клиентский код для push — гораздо сложнее, полностью с этим согласен.
K>Я, к сожалению не могу сейчас аргументированно доказать что вот прям pull парсинг крутая вещь. K>Но на своей задаче я пробовал pull парсинг, это сделало код обработки проще и быстрее, вот всё что я могку сказать.
Так и должно быть. Но вы сами себе где-то противоречите: "Более низкоуровневое средство требует более высокой дисциплины"
Кстати, я делал такую вещь:
Дано: здоровые ХМЛ файлы с довольно запутанной схемой. Писать SAX — времени нет.
Решение: .НЕТ-овским pull-парсером "режу" на элементы (довольно крупные, скажем такой элемент может быть 1КБ-1МБ, смотря что из себя представляет), а эти элементы — парсить в Xerces-C DOM — дальше — банально, код для работы уже с DOM-ом легко пишут студенты, в несколько потоков (каждому — по DOM-у). Прекрасно работает. Естественно, что DOM-ы не накапливаются в памяти, а после обработки дискардятся.
Re[6]: Большой XML файл.
От:
Аноним
Дата:
01.11.06 09:23
Оценка:
Здравствуйте, korzhik, Вы писали:
K>я понял о чём вы. K>c-smile pull parser не работает с namespace и entity но в тестируем файле этого и не было.
А вы добавьте к нем у код для работы с неймспейсами и пр. — и он станет медленнее. Потому как даже если неймспейсов и нет в инпуте — какая-то часть кода для их обработки (обнаружения наличия/отсутсвия) всё равно задействуется. И т.д и т.п. Не говоря уже о том, что код парсера станет гораздо сложее — труднее оптимизировать.
Разве коррекно сравнивать скорость работы Notepad и Word, даже если вы проводите тест используя plain text файлы? Думается, что Notepad такой тест легко выиграет, но его функционала как правило не хватает (уже для работы с RTF).
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, korzhik, Вы писали:
K>>я понял о чём вы. K>>c-smile pull parser не работает с namespace и entity но в тестируем файле этого и не было. А>А вы добавьте к нем у код для работы с неймспейсами и пр. — и он станет медленнее. Потому как даже если неймспейсов и нет в инпуте — какая-то часть кода для их обработки (обнаружения наличия/отсутсвия) всё равно задействуется. И т.д и т.п. Не говоря уже о том, что код парсера станет гораздо сложее — труднее оптимизировать.
А>Разве коррекно сравнивать скорость работы Notepad и Word, даже если вы проводите тест используя plain text файлы? Думается, что Notepad такой тест легко выиграет, но его функционала как правило не хватает (уже для работы с RTF).
Если я работаю с plain text файлами и мне нужно сравнить кто быстрее работает с plain text, word или notepad то такое сравнение корректно
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, korzhik, Вы писали:
K>>я понял о чём вы. K>>c-smile pull parser не работает с namespace и entity но в тестируем файле этого и не было. А>А вы добавьте к нем у код для работы с неймспейсами и пр. — и он станет медленнее. Потому как даже если неймспейсов и нет в инпуте — какая-то часть кода для их обработки (обнаружения наличия/отсутсвия) всё равно задействуется. И т.д и т.п. Не говоря уже о том, что код парсера станет гораздо сложее — труднее оптимизировать.
я уже говорил что пустой прогон expat парсера на 30mb файле, то есть просто парсинг без обработки, занимает очень мало времени что-то около 0.01с, в моём случае меня уже не интерисует будет это время 0.01 или 0.001
Тут ускорение получилось засчёт другого принципа обработки.
Re[8]: Большой XML файл.
От:
Аноним
Дата:
01.11.06 09:52
Оценка:
Здравствуйте, korzhik, Вы писали:
K>Если я работаю с plain text файлами и мне нужно сравнить кто быстрее работает с plain text, word или notepad то такое сравнение корректно
Да. Но делть из этого выводы о том, что pull быстрее чем push или наоборот — нет оснований.
И вообще, что мы обсуждаем: с какой стати должна быть разница в скорости между pull и push.
У меня есть кой-какой опыт в работе со здоровенными файлами всех мастей и он мне говорит, что скорость парсинга — дело десятое. То, что делется с распарсенными данными всё равно занимает гораздо больше (порядок-два) времени чем сам парсинг. Тот же ХМЛ, с точки зрения скорости, можно было бы и в DOM парсить — если бы памяти хватало.
Кстати, иногда мне хотелось DOM, который хранил бы дерево на диске (не в виде своп-файла , естественно, а чуть более оптимизированно для DOM целей — назовём это DOM DB) — никто такого не знает? ХМЛ DB-s — малость не то — функциональный оверхед, производительность не устраивает. Да и реально работает только Berkeley, остальные (все мне известные написаны на Яве) мрут от файлов в уже в десяток мегабайт или даже меньше, но со сложной структурой.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, korzhik, Вы писали:
K>>Если я работаю с plain text файлами и мне нужно сравнить кто быстрее работает с plain text, word или notepad то такое сравнение корректно А>Да. Но делть из этого выводы о том, что pull быстрее чем push или наоборот — нет оснований. А>И вообще, что мы обсуждаем: с какой стати должна быть разница в скорости между pull и push. А>У меня есть кой-какой опыт в работе со здоровенными файлами всех мастей и он мне говорит, что скорость парсинга — дело десятое. То, что делется с распарсенными данными всё равно занимает гораздо больше (порядок-два) времени чем сам парсинг. Тот же ХМЛ, с точки зрения
скорости, можно было бы и в DOM парсить — если бы памяти хватало.
Здравствуйте, korzhik, Вы писали:
K>pull парсер средство более низкоуровневое чем SAX. И наверняка SAX парсеры строятся на основе pull парсера. Более низкоуровневое средство требует более высокой дисциплины
Тот парсер что я выложил на codeproject есть SGML tokenizer на самом деле.
Т.е. им можно обрабатывать и XML и HTML.
Изначальный tokenizer был написан для некоего аппаратного firewall который "фильтрует HTML базар"
и делает это со скоростью соизмеримой со скоростью работы сетевых карт.
Данный парсер не аллоцирует память в прцессе работы — это было одно из условий.
Это (в том числе) увеличивает скорость обработки особенно в multithreaded средах.
Этот парсер имеет смысл использовать при обработке XML в случаях когда
1) известно что входной XML валиден (например для программного XML persistence/SOAP/etc.) и/или
2) объектная модель отличается от XML DOM model — например восстановление состояния программы из XML.
3) требуется потоковая обработка в процессе поступления данных.
Строго говоря только полный DOM парсер (xml text -> xml DOM) имеет право называться валидирующим.
пользоваться результатами работы SAX парсера нельзя пока не получен закрывающий тэг от root node. Частичный XML не валиден по определению.
Валидация и namespace handling в системах использующих мой сканнер выполняется кодом восстанавливающим объектную модель из потока.
pull parser это в принципе tagged input stream. Т.е. работа с ним это классическое восстановление данных из входного потока.
Вполне себе С++ в COM-стиле. Невалидирующий. Поддерживает namespaces.
Понимает несколько кодировок (можно расширять их кол-во).
Умеет писАть. Одно огорчает — не в исходниках Ах, и второе — не
кроссплатформен.
Прочел соседние ветки по поводу "что быстрее или удобнее и чем"...
IMHO у pull парсера есть дополнитнльное преимущество.
Это чуть большая гибкость в обработке ошибок. Допустим, в момент
получения определенного тэга или атрибута state вашей системы таков, что
всё, баста, хотим немедленно прерваться и, допустим, бросить исключение.
В случае с SAX парсерами — это чревато, т.о. в корне не правильно.
В случае с pull — тривиально. Получили от парсера данные,
проанализировали, закрыли парсер([RAII]) и дальше, что душе угодно —
хоть exception кидай, хоть longjmp с raise
Понятно, что и для SAX совсем не сложно запомнить ошибку, "оборвать"
парсинг и сделать всё тоже самое в охватывающем коде. Но, несколько
лишних телодвижений, особенно, если в callbackах "глубокая" логика...