Во-первых, SAX не относится к W3C стандарту обработки XML-документов, во-вторых, он относится к семейству потоковых парсеров, то есть данные из XML-документа потоком передаются в парсер и кусками обрабатывается в нем, при этом возможности вернуться и внести изменения не предоставляется.
Парсинг происходит на основе так называемых событий, которые обрабатываются обработчиками. События происходят, когда по ходу обработки документа встречаются открывающие и закрывающие теги, нахождение парсером текстового содержания узла, для обработки которого также определена функция-обработчик. Стоит отметить, что парсер основан на расширении expat (C-шный парсер), который при парсинге документа автоматом переводит имена тегов в верхний регистр, поэтому в функциях-обработчиках стоит использовать имена в верхнем регистре. Не стоит забывать и о кодировке. Все-таки, лучший вариант - utf-8. Ниже приведен скрипт для примера SAX-парсинга новостной ленты.
<?php #SAX-парсинг новостной ленты
/* Инициализация переменных.
Следующая переменная будет служить флагом, который указывает,
находится ли парсер внутри тега item или нет. Только внутри
этого тега находится информация, которую мы хотим получить
из новостной ленты.
*/
$insideitem = false;
/*
Следующие переменные будут использованы для хранения содержания
соответствующих XML-элементов. Эти переменные внутри функций
обработки будут объявлены глобальными.
*/
$tag = "";
$title = "";
$description = "";
$link = "";
/*
Функция обработки открывающего тега принимает в качестве
второго аргумента имя тега и проверяет, не является ли этот тег
тегом item. В случае успеха устанавливает флаг в переменной
$insideitem. Парсер преобразует имена тегов к верхнему регистру,
поэтому их и приходится сравнивать в таком виде.
*/
function startElement($parser, $name, $attrs) {
global $insideitem, $tag, $title, $description, $link;
if ($insideitem) {
$tag = $name;
} elseif ($name == "ITEM") {
$insideitem = true;
}
}
/*
Функция обработки закрывающего тега принимает в качестве второго
аргумента имя тега, проверяет, не является ли этот тег тегом item.
В случае успеха распечатывает данные, накопленные в переменных
$title, $description и $link, а затем присваивает этим переменным
значение "пустая строка". Перед распечаткой значения переменных
обрабатываются функцией trim(), отсекающей пробелы в начале и
конце строки, функцией htmlspecialchars(), обезвреживающей
содержимое элементов, и функцией iconv(), преобразующей данные
в кодировку Windows-1251.
*/
function endElement($parser, $name) {
global $insideitem, $tag, $title, $description, $link;
if ($name == "ITEM") {
$description = htmlspecialchars(trim($description));
$title = htmlspecialchars(trim($title));
printf("<dt><b><a href='%s'>%s</a></b></dt>", trim($link), $title);
printf("<dd>%s</dd>", $description);
$title = "";
$description = "";
$link = "";
$insideitem = false;
}
}
/*
Функция обработки символьного содержимого XML-элемента выполняет
действия только в случае, когда событие происходит внутри тега item,
это определяется по состоянию флага. В случае успеха анализируется,
на каком теге произошло событие, и текстовое содержимое элемента,
переданное функции во втором аргументе, добавляется к значению
соответствующей переменной.
*/
function characterData($parser, $data) {
global $insideitem, $tag, $title, $description, $link;
if ($insideitem) {
switch ($tag) {
case "TITLE":
$title .= $data;
break;
case "DESCRIPTION":
$description .= $data;
break;
case "LINK":
$link .= $data;
break;
}
}
}
// создание парсера
$xml_parser = xml_parser_create();
// задание функций-обработчиков
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
/*
Открытие файла RSS-ленты. В случае ошибки сработает оператор die,
вызвающий фукнцию sprintf(), которая распечатает сообщение об ошибке.
*/
$fp = fopen("rss2.xml","r") or die("Error reading RSS data.");
// Чтение четырех килобайтов данных из файла
while ($data = fread($fp, 4096))
// Вызов парсера для обработки данных из файла.
xml_parse($xml_parser, $data, feof($fp))
or die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
// Закрытие файла с данными после того, как он прочитан полностью.
fclose($fp);
// Освобождение ресурсов парсера.
xml_parser_free($xml_parser);
?>
Новостная лента в формате XML-документа (rss2.xml)
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>HardwarePortal.ru: Новости HardwarePortal.ru</title>
<link>http://www.hwp.ru</link>
<description>Portal about computer`s "hard".</description>
<language>en</language>
<lastBuildDate>Mon, 28 Aug 2010 18:14:06 +0400</lastBuildDate>
<webMaster>likeoff@mail.ru</webMaster>
<image>
<title>HardwarePortal.ru</title>
<url>http://hwp.ru/img/logo.gif</url>
<link>http://www.hwp.ru.ru</link>
<width>100</width>
<height>100</height>
<description>HardwarePortal.ru</description>
</image>
<item>
<title>Notebooks</title>
<link>http://hwp.ru/scripts/news_show.php?5717</link>
<description>Fujitsu Siemens released notebooks with fantastic cpu performance</description>
<pubDate>Mon, 28 Aug 2010 16:00:54 +0400</pubDate>
</item>
<item>
<title>Canon</title>
<link>http://hwp.ru/scripts/news_show.php?5692</link>
<description>Canon presents new mirror photo cameras</description>
<pubDate>Thu, 24 Aug 2010 15:57:32 +0400</pubDate>
</item>
<item>
<title>Projector</title>
<link>http://hwp.ru/scripts/news_show.php?5685</link>
<description>Toshiba create new projectors</description>
<pubDate>Thu, 24 Aug 2010 10:34:53 +0400</pubDate>
</item>
</channel>
</rss>
Исходники можно скачать по данной ссылке - download.
C/C++. Преобразование типов указателей
Операция присваивания указателей различных типов представляет собой копирование адреса памяти содержащегося в них, то есть указателю в левой части назначается та же самая область памяти, что у правого указателя.
#include <stdio.h>
int main() {
int *p1;
double *p2 = (double*)p1;
printf("%p\n", p1); // 7FFD7000
printf("%p\n", p2); // 7FFD7000
return 0;
}
Для указателей разного типов эта область памяти по правилам интерпретации указателей рассматривается как заполненная переменными либо одного, либо другого типа. Область памяти (захват) может иметь различную структуру (байтовую, словную и др.) в зависимости от того, через какой указатель мы с ней работаем (имеется в виду тип данных указателя).
Присваивание значения указателя одного типа указателю другого типа сопровождается действием, которое называется в С преобразованием типа указателя, и которое в С++ обозначается всегда явно. На самом деле это действие является чистой фикцией (команды транслятором не генерируются). Транслятор просто запоминает, что тип указываемой переменной изменился, поэтому операции адресной арифметики и косвенного обращения нужно выполнять с учетом нового типа указателя.
Рассмотрим следующий пример. Проинициализируем переменную x типа short int и определим для нее указатель p. Напомним, что размер памяти выделяемый под переменные данного типа занимает sizeof(short int) = 2 байта. То есть 16 битов - 16 разрядов. Далее создадим указатель q, к которому приводится указатель p. Для типа данных int отводится sizeof(int) = 4 байта. То есть 32 бита - 32 разряда.
#include <stdio.h>
int main() {
short int x = 0;
short int *p = &x;
int *q = (int*)p;
// short int pointer address = 0022FF26
printf("short int pointer address = %p\n", p);
// int pointer address = 0022FF26
printf("int pointer address = %p\n", q);
// 0
printf("%d\n", *p);
// -152 (garbage)
printf("%d\n", *(++p));
// -9961472
printf("%d\n", *q);
return 0;
}
Как было сказано выше, в указатель q копируется адрес указателя p (что подтверждается выводом их адресов), так как у них разные типы, то адресная арифметика и косвенное обращение к памяти будет разным, поэтому каждый из них будет захватывать определенную их типом область памяти, то есть иметь разную структуру памяти. На примере видно, как ведут себя оба указателя: при разыменовании указатель p дает значение переменной x, это очевидно, указатель же q дает неожиданный результат -9961472, это связано с тем, что в область захвата памяти вошла неинициализированная область памяти, в котором лежит мусор, в примере мы обратились к этому мусору - там лежит значение -152. Стоит отметить, что в вашем случае у вас могут быть получены свои адреса и значения. Если же теперь представить полученные значения в бинарном виде и обратить внимание на типы данных указателей, то можно "увидеть" структуру рабочей области памяти, которая изображена на следующем рисунке.
Как известно функции динамического распределения (malloc, calloc, realloc) возвращают указатель типа void* на распределенную динамически память. Указатели данного типа могут быть приведены к абсолютно любому типу, но операция по извлечению данных по адресу запрещены. Из вышеприведенного ясно, что в этом случае мы столкнемся с неопределенностью в области захвата памяти!
Модифицируем вышеприведенный пример так, что бы по второму указателю при разыменовании было исключено взятие мусора
#include <stdio.h>
int main() {
short int x[2] = {0, 0};
short int *p = x;
int *q = (int*)p;
// short int pointer address = 0022FF24
printf("short int pointer address = %p\n", p);
// int pointer address = 0022FF24
printf("int pointer address = %p\n", q);
// 0
printf("%d\n", *p);
// 0
printf("%d\n", *q);
return 0;
}
За счет использования массива, мы смогли проинициализировать рабочую область памяти. Как итог, снова и снова убеждаемся в необходимости соблюдать аккуратность и внимательность при работе с указателями. Всем удачи в нашем нелегком ремесле :)
#include <stdio.h>
int main() {
int *p1;
double *p2 = (double*)p1;
printf("%p\n", p1); // 7FFD7000
printf("%p\n", p2); // 7FFD7000
return 0;
}
Для указателей разного типов эта область памяти по правилам интерпретации указателей рассматривается как заполненная переменными либо одного, либо другого типа. Область памяти (захват) может иметь различную структуру (байтовую, словную и др.) в зависимости от того, через какой указатель мы с ней работаем (имеется в виду тип данных указателя).
Присваивание значения указателя одного типа указателю другого типа сопровождается действием, которое называется в С преобразованием типа указателя, и которое в С++ обозначается всегда явно. На самом деле это действие является чистой фикцией (команды транслятором не генерируются). Транслятор просто запоминает, что тип указываемой переменной изменился, поэтому операции адресной арифметики и косвенного обращения нужно выполнять с учетом нового типа указателя.
Рассмотрим следующий пример. Проинициализируем переменную x типа short int и определим для нее указатель p. Напомним, что размер памяти выделяемый под переменные данного типа занимает sizeof(short int) = 2 байта. То есть 16 битов - 16 разрядов. Далее создадим указатель q, к которому приводится указатель p. Для типа данных int отводится sizeof(int) = 4 байта. То есть 32 бита - 32 разряда.
#include <stdio.h>
int main() {
short int x = 0;
short int *p = &x;
int *q = (int*)p;
// short int pointer address = 0022FF26
printf("short int pointer address = %p\n", p);
// int pointer address = 0022FF26
printf("int pointer address = %p\n", q);
// 0
printf("%d\n", *p);
// -152 (garbage)
printf("%d\n", *(++p));
// -9961472
printf("%d\n", *q);
return 0;
}
Как было сказано выше, в указатель q копируется адрес указателя p (что подтверждается выводом их адресов), так как у них разные типы, то адресная арифметика и косвенное обращение к памяти будет разным, поэтому каждый из них будет захватывать определенную их типом область памяти, то есть иметь разную структуру памяти. На примере видно, как ведут себя оба указателя: при разыменовании указатель p дает значение переменной x, это очевидно, указатель же q дает неожиданный результат -9961472, это связано с тем, что в область захвата памяти вошла неинициализированная область памяти, в котором лежит мусор, в примере мы обратились к этому мусору - там лежит значение -152. Стоит отметить, что в вашем случае у вас могут быть получены свои адреса и значения. Если же теперь представить полученные значения в бинарном виде и обратить внимание на типы данных указателей, то можно "увидеть" структуру рабочей области памяти, которая изображена на следующем рисунке.
Как известно функции динамического распределения (malloc, calloc, realloc) возвращают указатель типа void* на распределенную динамически память. Указатели данного типа могут быть приведены к абсолютно любому типу, но операция по извлечению данных по адресу запрещены. Из вышеприведенного ясно, что в этом случае мы столкнемся с неопределенностью в области захвата памяти!
Модифицируем вышеприведенный пример так, что бы по второму указателю при разыменовании было исключено взятие мусора
#include <stdio.h>
int main() {
short int x[2] = {0, 0};
short int *p = x;
int *q = (int*)p;
// short int pointer address = 0022FF24
printf("short int pointer address = %p\n", p);
// int pointer address = 0022FF24
printf("int pointer address = %p\n", q);
// 0
printf("%d\n", *p);
// 0
printf("%d\n", *q);
return 0;
}
За счет использования массива, мы смогли проинициализировать рабочую область памяти. Как итог, снова и снова убеждаемся в необходимости соблюдать аккуратность и внимательность при работе с указателями. Всем удачи в нашем нелегком ремесле :)
C/C++. Арифметика указателей
Арифметика указателей проста в силу их "адресной" природы:
- указатели можно складывать и вычитать с константными целочисленными значениями, например вот так:
#include <stdio.h>
int main() {
int array[5], *ptr1, *ptr2, size;
ptr1 = &array[0];
ptr2 = ptr1 + 5;
size = ptr2 - ptr1;
// size = 5
printf("\nsize = %d", size);
return 0;
}
или вот так:
#include <stdio.h
int main() {
int array[5], *ptr1, *ptr2, size;
ptr1 = &array[5];
ptr2 = ptr1 - 5;
size = ptr2 - ptr1;
// size = -5
printf("\nsize = %d", size);
return 0;
}
- запрещено складывать указатели друг на друга, но разрешено вычитать, эту особенность удобно использовать при вычислении элементов массивов:
#include <stdio.h>
int main() {
int array[5], *ptr1, *ptr2, size;
ptr1 = &array[0];
ptr2 = &array[5];
// складывать указатели запрещено
size = ptr2 - ptr1;
// в данном случае не приходится дополнительно учитывать тип массива
printf("\n1. size = %d", size);
// альтернативные варианты
printf("\n2. size = %d", sizeof(array)/sizeof(&array));
printf("\n2. size = %d", sizeof(array)/sizeof(array[0]));
printf("\n3. size = %d", sizeof(array)/sizeof(&array[0]));
printf("\n4. size = %d", sizeof(array)/sizeof(int));
return 0;
}
Обратите внимание, в данном случае не нужно дополнительно указывать тип массива, в отличии от альтернативных способов. Обратите внимание на тип переменной size - int - ее тип соответствует типу вычитаемых указателей, вообще для разности указателей выделяют специальный тип - ptrdiff_t в stddef.h. Этот тип используют для получения одинакового поведения (результата) данной реализации на разных платформах. Ниже приведен пример с учетом данного типа:
#include <stdio.h>
#include <stddef.h>
int main() {
int array[5], *ptr1, *ptr2;
ptr1 = &array[0];
ptr2 = &array[5];
// складывать указатели запрещено
ptrdiff_t size = ptr2 - ptr1;
// в данном случае не приходится дополнительно учитывать тип массива
printf("\n1. size = %d", size);
// альтернативные варианты
printf("\n2. size = %d", sizeof(array)/sizeof(&array));
printf("\n2. size = %d", sizeof(array)/sizeof(array[0]));
printf("\n3. size = %d", sizeof(array)/sizeof(&array[0]));
printf("\n4. size = %d", sizeof(array)/sizeof(int));
return 0;
}
- указатели можно складывать и вычитать с константными целочисленными значениями, например вот так:
#include <stdio.h>
int main() {
int array[5], *ptr1, *ptr2, size;
ptr1 = &array[0];
ptr2 = ptr1 + 5;
size = ptr2 - ptr1;
// size = 5
printf("\nsize = %d", size);
return 0;
}
или вот так:
#include <stdio.h
int main() {
int array[5], *ptr1, *ptr2, size;
ptr1 = &array[5];
ptr2 = ptr1 - 5;
size = ptr2 - ptr1;
// size = -5
printf("\nsize = %d", size);
return 0;
}
- запрещено складывать указатели друг на друга, но разрешено вычитать, эту особенность удобно использовать при вычислении элементов массивов:
#include <stdio.h>
int main() {
int array[5], *ptr1, *ptr2, size;
ptr1 = &array[0];
ptr2 = &array[5];
// складывать указатели запрещено
size = ptr2 - ptr1;
// в данном случае не приходится дополнительно учитывать тип массива
printf("\n1. size = %d", size);
// альтернативные варианты
printf("\n2. size = %d", sizeof(array)/sizeof(&array));
printf("\n2. size = %d", sizeof(array)/sizeof(array[0]));
printf("\n3. size = %d", sizeof(array)/sizeof(&array[0]));
printf("\n4. size = %d", sizeof(array)/sizeof(int));
return 0;
}
Обратите внимание, в данном случае не нужно дополнительно указывать тип массива, в отличии от альтернативных способов. Обратите внимание на тип переменной size - int - ее тип соответствует типу вычитаемых указателей, вообще для разности указателей выделяют специальный тип - ptrdiff_t в stddef.h. Этот тип используют для получения одинакового поведения (результата) данной реализации на разных платформах. Ниже приведен пример с учетом данного типа:
#include <stdio.h>
#include <stddef.h>
int main() {
int array[5], *ptr1, *ptr2;
ptr1 = &array[0];
ptr2 = &array[5];
// складывать указатели запрещено
ptrdiff_t size = ptr2 - ptr1;
// в данном случае не приходится дополнительно учитывать тип массива
printf("\n1. size = %d", size);
// альтернативные варианты
printf("\n2. size = %d", sizeof(array)/sizeof(&array));
printf("\n2. size = %d", sizeof(array)/sizeof(array[0]));
printf("\n3. size = %d", sizeof(array)/sizeof(&array[0]));
printf("\n4. size = %d", sizeof(array)/sizeof(int));
return 0;
}
Subscribe to:
Posts
(
Atom
)