Как сделать 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-файлов. Я еще слышал про какие-то другие более мощные методы, но их буду рассматривать, когда сам изучу немного
Популярность: 100%

Июль 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