Как сделать xml parser.
Вот и лето пришло и первая неделя июля пролетела незаметно. Через две недели мне защищать свой диплом, а одна из частей его - XML parser. Но жалко, что не в пхп. Ну ниче, счас наверстаем
Я видел много xml parser`ов, но не затрагивал при этом веб-программирование. Теперь же я хочу выяснить и научиться вместе с вами, как сделать простой xml parser в php.
А зачем? Надо!
Не, ну на самом деле: xml-файлы - очень полезная штука. И любой профессионал должен… нет, не должен, а обязан знать, как с ними работать. Мы же хотим стать профессионалами? Если Вы на моем блоге, то такое желание у Вас есть.
Итак…
Мы предполагаем, что знаем, что такое XML и описывать его здесь не будем. Ну, если не знаем, то легко узнаем здесь: http://ru.wikipedia.org/wiki/XML
При поиске способов парсинга XML на PHP, я обнаружил простой набор функций в PHP для работы с XML-файлами, который называется «XML Parser Functions». Парсинг начинается с инициализации парсера вызовом функции xml_parser_create:
$xml_parser = xml_parser_create();
Потом нам нужно сказать парсеру, какие функции будут обрабатывать попадающиеся ему xml-теги и текстовую информацию в процессе парсинга. Т.е. нужно установить некие обработчики:
xml_set_element_handler($xml_parser, “startElement”, “endElement”);
Эта функция отвечает за установку обработчиков начала элемента и конца элемента. Например, если в тексте xml-файла встретится комбинация
Сами же функции startElement и endElement принимают несколько параметров согласно документации по php:
function startElement($parser, $name, $attrs) {
// $parser - уникальный идентификатор парсера
// (т.к. мы можем использовать несколько парсеров)
// $name - имя обнаруженного элемента
// $attrs - массив атрибутов обнаруженного элемента
}
function endElement($parser, $name) {
// $parser - уникальный идентификатор парсера
// $name - имя обнаруженного элемента
}
?>
А как же считывать данные из файла? Мы же пока не видели ни одного параметра для этого ни в одной из функций! А об этом дальше: считывание файла возлагается на плечи программиста, т.е. мы должны использовать стандартные функции для работы с файлами:
Открыли файл. А теперь нужно построчно считывать его и скармливать считываемые строки функции xml_parse:
while ($data = fgets($fp)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
echo "<br>XML Error: ".xml_error_string(xml_get_error_code($xml_parser));
echo " at line ".xml_get_current_line_number($xml_parser);
break;
}
}
?>
Здесь заметим две очень важные вещи. Первая - это то, что функции xml_parse в третьем параметре нужно передать флаг считывания последней строки (true - если строка последняя, false - если нет). Второе - это то, что как и в любом деле, мы должны следить здесь за ошибками. За это отвечают функции xml_get_error_code и xml_error_string. Первая функция получает код ошибки, а вторая - по полученному коду возвращает текстовое описание ошибки. Что в результате возникновения ошибки получится - рассмотрим позже. Не менее полезная функция xml_get_current_line_number скажет нам номер текущей обрабатываемой строки в файле.
И как всегда мы должны освободить занимаемые системой ресурсы. Для парсинга XML - это функция xml_parser_free:
xml_parser_free($xml_parser);
Вот, основные функции мы рассмотрели. Пора бы посмотреть их на деле. Для этого я придумал xml-файл с очень простой структурой:
<root>
<info who="моя">
<address ulica="моя улица!!" kvartira="12" dom="15">123</address>
<phone>+71234567890</phone>
</info>
</root>
Назовем этот файл data.xml и попытаемся его распарсить с помощью следующего кода:
function startElement($parser, $name, $attrs) {
global $depth;
echo str_repeat(" ", $depth * 3); // отступы
echo "<b>Element: $name</b><br>"; // имя элемента
$depth++; // увеличиваем глубину, чтобы браузер показал отступы
foreach ($attrs as $attr => $value) {
echo str_repeat(" ", $depth * 3); // отступы
// выводим имя атрибута и его значение
echo 'Attribute: '.$attr.' = '.$value.'<br>';
}
}
function endElement($parser, $name) {
global $depth;
$depth--; // уменьшаем глубину
}
$depth = 0;
$file = "data.xml";
$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
if (!($fp = fopen($file, "r"))) {
die("could not open XML input");
}
while ($data = fgets($fp)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
echo "<br>XML Error: ";
echo xml_error_string(xml_get_error_code($xml_parser));
echo " at line ".xml_get_current_line_number($xml_parser);
break;
}
}
xml_parser_free($xml_parser);
?>
В результате разработанного нами простейшего скрипта браузер вывел в свое окно следующую информацию:
Element: ROOT
Element: INFO
Attribute: WHO = моя
Element: ADDRESS
Attribute: ULICA = моя улица!!
Attribute: KVARTIRA = 12
Attribute: DOM = 15
Element: PHONE
Попробуем испортить XML-файл, заменив тег <phone> на <telephone>, а закрывающий тег оставив прежним:
Element: ROOT
Element: INFO
Attribute: WHO = моя
Element: ADDRESS
Attribute: ULICA = моя улица!!
Attribute: KVARTIRA = 12
Attribute: DOM = 15
Element: TELEPHONE
XML Error: Mismatched tag at line 5
Ух ты! Сообщения об ошибках работают! Причем довольно информативные.
Эх, я забыл еще одну вещь… Мы же не вывели текст, содержащийся внутри тегов address и phone. Исправляем наш недочет - добавляем текстовый обработчик с помощью функции xml_set_character_data_handler:
xml_set_character_data_handler($xml_parser, ’stringElement’);
И добавляем в код саму функцию-обработчик:
Посмотрим теперь на вывод:
Element: ROOT
Element: INFO
Attribute: WHO = моя
Element: ADDRESS
Attribute: ULICA = моя улица!!
Attribute: KVARTIRA = 12
Attribute: DOM = 15
String: 123
Element: PHONE
String: +71234567890
О! Теперь вывели все!
Кстати, кто-нибудь заметил, что имена тегов и атрибутов все большими буквами написаны? Странно… они же в нашем xml-файле малыми буквами обозначены. Видимо где-то какие-то настройки установлены, чтобы делать uppercase…
Ааа, нашел! Оказывается есть еще функция xml_parser_set_option:
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
Таким вызовом мы отменяем вывод имен атрибутов и имен тегов большими буквами:
Element: root
Element: info
Attribute: who = моя
Element: address
Attribute: ulica = моя улица!!
Attribute: kvartira = 12
Attribute: dom = 15
String: 123
Element: phone
String: +71234567890
В этой статье мы рассмотрели самый простой, но для большинства задач достаточный метод вытаскивания информации из XML-файлов. Я еще слышал про какие-то другие более мощные методы, но их буду рассматривать, когда сам изучу немного
Популярность: 21%


Июль 26th, 2008
По мне так куда проще работать со стандартным DOM (для php5, DOM XML для php4), кроме всего прочего с ним отлично работает XSL (для php5, XSLT для php 4)
Сентябрь 8th, 2008
Боюсь вы не сделали XML parser, а просто воспользовались готовым.
PS: SAX вообще говоря довольно интересный инструмент для того чтобы обрабатывать большие документы и не разбазаривать память на построение DOM, но почему-то о нем не все помнят.
Сентябрь 8th, 2008
В основе механизма SAX лежит принцип поточной обработки xml. Он как раз используется для обработки больших файлов. Не используют его в таких случаях только по неопытности думаю
Сентябрь 18th, 2008
Простите, а это “с нуля” или Вы чей-то скрипт разобрали?
Сентябрь 18th, 2008
Конечно с нуля. Я же просто использовал базовые возможности. Грех не написать с помощью них парсер самому.
Октябрь 3rd, 2008
Может кто-нибудь знает наилучший способ парсинга документов с ошибками на подобие: Mismatched tag. А то в интернете страницы без ошибок - это редкое исулючение…
Октябрь 24th, 2008
гут статья)
Октябрь 31st, 2008
У меня возникла проблема с русскими символами в xml файле. PHP не хочет их парсит, вылетая с ошибкой:
Element: ROOT
XML Error: Invalid character at line 3
Подскажите пожалуйста что делать.
Октябрь 31st, 2008
Андрей, попробуйте сохранить xml-файл в кодировке UTF-8 без сигнатуры (without BOM).
Ноябрь 11th, 2008
у меня не работает вывод String куда именно нужно вставить последнюю функцию, помогите пожалуйста?
Ноябрь 11th, 2008
nika, Вам нужно вставить вызов
xml_set_character_data_handler($xml_parser, ’stringElement’);
после
xml_set_element_handler($xml_parser, “startElement”, “endElement”);
Ноябрь 11th, 2008
да и еще такой вопрос а что нужно сделать, чтобы русские буквы отображались нормально, а не “кракозябрами”
Ноябрь 11th, 2008
nika, попробуйте сохранить xml-файл в кодировке UTF-8 без сигнатуры (without BOM)
Ноябрь 11th, 2008
novice, у меня “кракозябры” при выводе уже запарсенного файла
Ноябрь 11th, 2008
В общем для правильного отображения нужно соблюдать следующее:
1. нужно, чтобы xml-файл был в кодировке UTF-8
2. в xml-файле первая строчка должна быть такой:
< ?xml version="1.0" encoding="UTF-8"?>
3. нужно инициализировать парсер так:
xml_parser_create(”UTF-8″);
4. нужно перед выводом содержимого xml-файла в браузер настроить последний на кодировку UTF-8:
header( “Content-Type: text/html; charset=utf-8″);
Ноябрь 12th, 2008
а куда именно вставить вот эту строчку
header(”Content-Type: text/html; charset=utf-8″);
я поставила в начале всех функций, но выдается ошибка
Ноябрь 12th, 2008
Эту строчку нужно вставить перед выводом информации на экран. А какая ошибка выдается?
Ноябрь 14th, 2008
Спасибо огромное за парсер!!!
нашла ошибочку ’stringElement’ сделайте в тексте с двойными кавычками - а то у меня вывод String тоже не сразу заработал :)) ещё раз спасибо!
Ноябрь 14th, 2008
Тут просто нужно быть аккуратнее с кавычками. ’stringElement’, `stringElement` и ’stringElement’ - не одно и то же с точки зрения PHP
Ноябрь 14th, 2008
novice, у меня вылетает вот такая ошибка Warning: Cannot modify header information - headers already sent by (output started at Z:\home\parser\www\index.php:9) in Z:\home\parser\www\index.php on line 10
10 линия это как раз header
а на 9 начинается php, то есть вот такой код: <?php
Декабрь 11th, 2008
Спасибо большое! Сейчас столкнулся с подобной проблемой (нужно распарсить базу 1С-ки и перегнать её в удобочитаемый формат для своей CMS-ки), а у вас все просто и понятно расписано!
p.s.: а еще спасибо за урок по сокетам!
Декабрь 11th, 2008
Пожалуйста
Декабрь 14th, 2008
Отличная статья. Спасибо
Декабрь 21st, 2008
Статья класс, побольше бы таких. Кто знает как теперь это всё добавить в базу данных, каждый элемент, атрибут в разные столбцы?
Декабрь 29th, 2008
Вот я как раз тоже озадачен такой проблемой импорт из xml в php… нарою что нить отпишу
Декабрь 29th, 2008
Заранее большое спасибо. Кстати я сделал с помощью XMLReader но добавляет 200.000 записей на localhost примерно 30 минут по идее за 3 минуты должно.
Декабрь 29th, 2008
Mark, попробуйте ради эксперимента сделать то же самое на SimpleXML или с помощью другого PHP-инструмента
Январь 6th, 2009
Спасибо, Вы указали направление.
Январь 27th, 2009
ага, и попробуйте потом с simplexml, безгеморно, например из
…
Просто Текстовая нода, а это ещё одна текстовая
нода с сылкой
…
вытищить нотацией -> или [] значения нод и , и особенно, значения их аттрибутов.
повторяю - безгеморно
так что SAX, рулит и не только для док-тов не больше 5 МБ, а так же и для всех тех, которые имеют так называемый “смешанный-XML контент”
Январь 27th, 2009
мдя… мой вам совет - прикрутите парсинг ввода пользователя
Февраль 25th, 2009
Для отображения текста на русском языке используем
iconv(”UTF-8″,”windows-1251″, $attr), где $attr, то что нужно вывести.
Апрель 2nd, 2009
Спасибо, хорошая статья. У меня получилось всё и сразу. Дальше надо делать выборки из списка. На эту тему бы статейку, а? Например, есть группы товаров. Выводим только группы. Или список внутри группы.
Апрель 2nd, 2009
Для этого, как мне кажется, лучше воспользоваться SimpleXML. Там удобнее будет связи соблюдать. А насчет статьи - может как-нибудь напишу
Апрель 6th, 2009
Спасиб, хорошая статья!!!!
Вот еще бы статейку наподобие, только как редактировать xml с php!
Апрель 20th, 2009
Есть такие два класса интересных в стандартной библеотеке PHP5 - DOMDocument() и XSLTProcesor() использовал на довольно крупных проектах - не жалуюсь =)
Апрель 27th, 2009
спасибо! полезная статейка! мне сейчас особенно нужно было решение как парсить xml с помощью php, поэтому статейка пригодилась! спасибо!
Май 6th, 2009
Есть такая проблема. При выводе String длинной более 174 символов происходит разбиение текста на несколько String(по 174). Где может быть данное ограничение и как его преодолеть?
Май 15th, 2009
Очень помогло. Автору респект.
Июнь 7th, 2009
Отличная статья очень помогла. Давно искал такой парсер!
Кладу в капилку денюжку
Июнь 7th, 2009
mobon, спасибо!
Июнь 25th, 2009
Вообще у PHP есть еще одна библиотечка для разбора XML по принципу SAX - называется XMLReader.