PHP4/PHP5. Simple Application Programming Interface for XML. SAX-parser

Во-первых, 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 &quot;hard&quot;.</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;


}


За счет использования массива, мы смогли проинициализировать рабочую область памяти. Как итог, снова и снова убеждаемся в необходимости соблюдать аккуратность и внимательность при работе с указателями. Всем удачи в нашем нелегком ремесле :)

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;
}