SamForum.org  
SamLab.ws
Заблокированные пользователи

Вернуться   SamForum.org > Программирование > С
Важная информация

Ответ
 
Опции темы Опции просмотра
Разбор XML (TinyXML) на С++
Старый Добавлено: 01.04.2011, 14:47
  (#1)
henrypp
old-school
Пользователь
 
Аватар для henrypp

По умолчанию Разбор XML (TinyXML) на С++

Есть XML документ примерного содержания:

XML документ

нужно получить всё что отмечено как "ТЕКСТ":

Код:
<user name="ТЕКСТ"/>

<userdata address="ТЕКСТ" telephone="ТЕКСТ"/>

<base>
<day date="ТЕКСТ" money="ТЕКСТ"/>
</base>
и всё это Unicode

есть тут кто разбирается в XML? Правильно ли я составил XML док.?

TinyXML


www.henrypp.org | github.com/henrypp | instagr.am/penmyak
henrypp вне форума Отправить личное сообщение для henrypp
Вверх
Ответить с цитированием
Старый Добавлено: 01.04.2011, 18:55
  (#2)
Exodus
Заблокирован
Заблокирован
 
Аватар для Exodus

По умолчанию

henrypp, написать preg_match или preg_replace как в PHP, только на чистом СИ... хотя можно и в лоб перебор по символам...
Exodus вне форума
Вверх
Ответить с цитированием
Старый Добавлено: 01.04.2011, 20:16
  (#3)
henrypp
old-school
Пользователь
 
Аватар для henrypp

По умолчанию

хмммм..... зачем? Я думаю эта библиотека может это сделать и без RegExp, вопрос в том - как?


www.henrypp.org | github.com/henrypp | instagr.am/penmyak
henrypp вне форума Отправить личное сообщение для henrypp
Вверх
Ответить с цитированием
Старый Добавлено: 01.04.2011, 23:29
  (#4)
Exodus
Заблокирован
Заблокирован
 
Аватар для Exodus

По умолчанию

Цитата:
Сообщение от henrypp Посмотреть сообщение
хмммм..... зачем? Я думаю эта библиотека может это сделать и без RegExp, вопрос в том - как?

так а документация?
[Ссылки могут видеть только зарегистрированные пользователи. ]
Exodus вне форума
Вверх
Ответить с цитированием
Этот пользователь сказал cпасибо за это полезное сообщение:
henrypp (03.04.2011)
Старый Добавлено: 01.04.2011, 23:50
  (#5)
henrypp
old-school
Пользователь
 
Аватар для henrypp

По умолчанию

У меня слишком "хитрый" Xml-Doc, я не понимаю как получить нужные данные

Код:
TiXmlDocument doc("database.xml");
TiXmlElement *root = doc.FirstChildElement("database");
не понимаю что дальше нужно? Документация? tl;dr


www.henrypp.org | github.com/henrypp | instagr.am/penmyak
henrypp вне форума Отправить личное сообщение для henrypp
Вверх
Ответить с цитированием
Старый Добавлено: 03.04.2011, 00:53
  (#6)
L.E.O.
Пользователь
Пользователь
 
Аватар для L.E.O.

По умолчанию

henrypp, не совсем понял, в чем проблема?
Вам необходимо с помощью TinyXML разобрать xml по вашему шаблону?
Если так, то вот пример кода:
Пример кода:

Код:
#include "tinyxml/tinystr.h"
#include "tinyxml/tinystr.cpp"
#include "tinyxml/tinyxml.h"
#include "tinyxml/tinyxml.cpp"
#include "tinyxml/tinyxmlerror.cpp"
#include "tinyxml/tinyxmlparser.cpp"

#include <iostream>

int main()
{
    // Загружаем xml с кодировкой UTF-8
    TiXmlDocument document("database.xml");
    document.LoadFile(TIXML_ENCODING_UTF8);

    // Загружаем категорию "<database>"
    TiXmlElement *database = document.FirstChildElement("database");
    if (database)
    {
        // Загружаем категорию "<user>" по этапно
        for (TiXmlElement *user = database->FirstChildElement("user"); user; user = user->NextSiblingElement("user"))
        {
            // Получаем name
            const char *name = user->Attribute("name");
            if (name)
            {
                std::cout << "Name: " << name << std::endl;
            }

            // Загружаем категорию "<userdata>"
            TiXmlElement *userdata = user->FirstChildElement("userdata");
            if (userdata)
            {
                // Получаем address
                const char *address = userdata->Attribute("address");
                if (address)
                {
                    std::cout << "\tAddress: " << address << std::endl;
                }

                // Получаем telephon
                const char *telephon = userdata->Attribute("telephone");
                if (telephon)
                {
                    std::cout << "\tTelephone: " << telephon << std::endl;
                }
            }

            // Загружаем категорию "<base>"
            TiXmlElement *base = user->FirstChildElement("base");
            if (base)
            {
                std::cout << "\tBase:" << std::endl;

                // Загружаем категорию "<day>" по этапно
                for (TiXmlElement *day = base->FirstChildElement("day"); day; day = day->NextSiblingElement("day"))
                {
                    // Получаем date
                    const char *date = day->Attribute("date");
                    if (date)
                    {
                        std::cout << "\t\tDate: " << date;
                    }

                    // Получаем money
                    const char *money = day->Attribute("money");
                    if (money)
                    {
                        std::cout << "\t\tMoney: " << money << std::endl;
                    }
                }
            }

            std::cout << std::endl;
        }
    }
    return 0;
}


Внимание!!! Писал под линуксом. Библиотека находилась в папке ".\tinyxml". Я ее при линковке не подключал. Поэтому include так много и имеет вид:
#include "tinyxml/*.*"

XML-документ немного поправил, чтобы результат работы был виден лучше:
XML-документ:

<?xml version='1.0' encoding="utf-8" standalone="yes"?>

<database>
<user name="Вася Пупкин">
<userdata address="Pushkin st. 26" telephone="+123123343"/>

<base>
<day date="25/03/11" money="5000"/>
<day date="25/04/11" money="5001"/>
<day date="25/05/11" money="5002"/>
<day date="25/06/11" money="5003"/>
</base>
</user>
<user name="Серёжа (!) Пупкин">
<userdata address="Pushkin st. 27" telephone="+123123343"/>

<base>
<day date="25/03/12" money="5004"/>
<day date="25/04/12" money="5005"/>
<day date="25/05/12" money="5006"/>
<day date="25/06/12" money="5007"/>
</base>
</user>
</database>


Этот xml имеет имя "database.xml" и находился в папке с программой.
Результат вывода в файл:
Результат в файле:

Код:
Name: Вася Пупкин
	Address: Pushkin st. 26
	Telephone: +123123343
	Base:
		Date: 25/03/11		Money: 5000
		Date: 25/04/11		Money: 5001
		Date: 25/05/11		Money: 5002
		Date: 25/06/11		Money: 5003

Name: Серёжа (!) Пупкин
	Address: Pushkin st. 27
	Telephone: +123123343
	Base:
		Date: 25/03/12		Money: 5004
		Date: 25/04/12		Money: 5005
		Date: 25/05/12		Money: 5006
		Date: 25/06/12		Money: 5007


Результат вывода в консоль:
Результат в консоле:

Код:
Name: Вася Пупкин
	Address: Pushkin st. 26
	Telephone: +123123343
	Base:
		Date: 25/03/11		Money: 5000
		Date: 25/04/11		Money: 5001
		Date: 25/05/11		Money: 5002
		Date: 25/06/11		Money: 5003

Name: Серёжа (!) Пупкин
	Address: Pushkin st. 27
	Telephone: +123123343
	Base:
		Date: 25/03/12		Money: 5004
		Date: 25/04/12		Money: 5005
		Date: 25/05/12		Money: 5006
		Date: 25/06/12		Money: 5007


Таким образом строка хранится в кодировке UTF-8. Это стопроцентно. Так что при выводе это не забываем


Помог, скажи спасибо
L.E.O. вне форума Отправить личное сообщение для L.E.O.
Вверх
Ответить с цитированием
Этот пользователь сказал cпасибо за это полезное сообщение:
henrypp (03.04.2011)
Старый Добавлено: 03.04.2011, 12:44
  (#7)
henrypp
old-school
Пользователь
 
Аватар для henrypp

По умолчанию

L.E.O., спасибо огромное!

Цитата:
XML-документ немного поправил, чтобы результат работы был виден лучше

только отступы убрал?

И ещё, в библиотеке типы данных char, хотя разработчики уверяют что она поддерживает Unicode, это как?


www.henrypp.org | github.com/henrypp | instagr.am/penmyak
henrypp вне форума Отправить личное сообщение для henrypp
Вверх
Ответить с цитированием
Старый Добавлено: 03.04.2011, 17:43
  (#8)
L.E.O.
Пользователь
Пользователь
 
Аватар для L.E.O.

По умолчанию

Цитата:
Сообщение от henrypp Посмотреть сообщение
только отступы убрал?

нет. Данные поправил. Раньше они были одинаковыми. Например:
Код:
		<base>
		  <day date="25/03/11" money="5000"/>
		  <day date="25/03/11" money="5000"/>
		  <day date="25/03/11" money="5000"/>
		  <day date="25/03/11" money="5000"/>
		</base>
Я изменил значение address, date и money. (Кстати, отступы я не убирал. Просто текст так отображается. Тег "code" забыл поставить)

Цитата:
Сообщение от henrypp Посмотреть сообщение
И ещё, в библиотеке типы данных char, хотя разработчики уверяют что она поддерживает Unicode, это как?

Типа char в библиотеке не встретил. Встретил тип "const char*", "char*". Это совершено разные типы. Опуская const; "char*" - означает массив символов элементы которого является одно-байтовый тип. То есть, фактически, это поток байтов. Строка в понимании C/C++ является потоком битов (в конечном итоге байтов), заканчивающимся символом с кодом 0. Как видно из определения поддержка Unicode может быть и с "char*".
Насчет юникода. Я знаю три их вида: UTF-7, UTF-8, UTF-16. UTF-16 это юникод, каждый символ которого кодируется в два байта. UTF-7 и UTF-8 это сжатые UTF-16 юникоды (подробнее можно почитать даже и на википедии) и длина каждого символа переменная величина от 8 битов до 16 битов. Так что кодировать всегда в двухбайтовые символы не верно, а вот кодировать в одно-байтовые верно, т.к. их можно воспринимать как биты.


Помог, скажи спасибо
L.E.O. вне форума Отправить личное сообщение для L.E.O.
Вверх
Ответить с цитированием
Этот пользователь сказал cпасибо за это полезное сообщение:
henrypp (03.04.2011)
Старый Добавлено: 03.04.2011, 22:34
  (#9)
henrypp
old-school
Пользователь
 
Аватар для henrypp

По умолчанию

А как тогда использовать возвращаемые значения методов (char'ы) TinyXML'а в Unicode функциях программы (к примеру MessageBoxW)? Преобразовывать? reinterpret_cast, MultiByteToWideChar. Не использовать же MessageBoxA


www.henrypp.org | github.com/henrypp | instagr.am/penmyak
henrypp вне форума Отправить личное сообщение для henrypp
Вверх
Ответить с цитированием
Старый Добавлено: 03.04.2011, 23:42
  (#10)
L.E.O.
Пользователь
Пользователь
 
Аватар для L.E.O.

По умолчанию

Начнем с того, что MessageBoxW использует UTF-16, а ваш XML-документ закодирован в UTF-8 (Это видно по заголовку: "<?xml version='1.0' encoding="utf-8" standalone="yes"?>"). И функции TinyXML поэтому тоже будут возвращать в этой кодировке. Так что в вашем случае обязательно нужно преобразовывать с помощью функции MultiByteToWideChar.

Если же ваш файл был в формате UTF-16, то можно было делать вот так:
Код:
    const wchar_t *wname = (const wchar_t *)user->Attribute("name");
Прим: я изменил одну строчку из первого примера.

Пример кода преобразования для вашего случаю в статическом виде (то есть максимальный размер строки для UTF-16 задан):
Код:
    const char *name = user->Attribute("name");
    int lenWideChar = 255;
    wchar_t wname[lenWideChar];
    lenWideChar = MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, lenWideChar);
Ну и используем wname, который имеет длину lenWideChar в функциях для юникода.

Если нужно для динамического случая (то есть максимальный размер строки для UTF-16 не задан и выделяется столько памяти, сколько нужно), сообщите, я его приведу.

PS: еще раз напомню. Юникодовские виндовские функции всегда работают только с UTF-16.


Помог, скажи спасибо
L.E.O. вне форума Отправить личное сообщение для L.E.O.
Вверх
Ответить с цитированием
Старый Добавлено: 04.04.2011, 00:41
  (#11)
Exodus
Заблокирован
Заблокирован
 
Аватар для Exodus

По умолчанию

Цитата:
Сообщение от L.E.O. Посмотреть сообщение
MultiByteToWideChar

а mbstowcs?
Exodus вне форума
Вверх
Ответить с цитированием
Этот пользователь сказал cпасибо за это полезное сообщение:
henrypp (07.04.2011)
Старый Добавлено: 04.04.2011, 11:23
  (#12)
L.E.O.
Пользователь
Пользователь
 
Аватар для L.E.O.

По умолчанию

Цитата:
Сообщение от Exodus Посмотреть сообщение
а mbstowcs?

Можно использовать и mbstowcs. Здесь кому как удобнее. С mbstowcs не забываем использовать setlocale.
Пример кода с mbstowcs. Используется динамический вид:

Код:
#include "tinyxml/tinystr.h"
#include "tinyxml/tinystr.cpp"
#include "tinyxml/tinyxml.h"
#include "tinyxml/tinyxml.cpp"
#include "tinyxml/tinyxmlerror.cpp"
#include "tinyxml/tinyxmlparser.cpp"

#include <locale.h>
#include <stdio.h>
#include <wchar.h>

// Функция перевода из char* в wchar_t*
void printf_wchar(const char *mb, const wchar_t *title, bool endOfLine)
{
    wchar_t *wcs = NULL;

    // Получаем необходимую длину для wchar_t*
    size_t requiredSize = mbstowcs(NULL, mb, 0);
    wcs = new wchar_t[requiredSize + 1];

    wprintf(title);

    if (mbstowcs(wcs, mb, requiredSize + 1) != size_t(-1)) // Пытаемся ее перекодировать
    {
        // Удачно :) выводим ее и освобождаем память
        wprintf(L": \"%ls\"", wcs);
        delete[] wcs;
    }
    else
    {
        // Неудачно :( Сообщаем об ошибке
        wprintf(L": \"%ls\"", L"не смог перевести в wchar_t*");
    }

    if (endOfLine)
    {
        wprintf(L"\n");
    }
}

int main()
{
    // Устанавливает кодировку UTF-8
    setlocale(LC_ALL, "UTF-8");

    // Загружаем xml с кодировкой UTF-8
    TiXmlDocument document("database.xml");
    document.LoadFile(TIXML_ENCODING_UTF8);

    // Загружаем категорию "<database>"
    TiXmlElement *database = document.FirstChildElement("database");
    if (database)
    {
        // Загружаем категорию "<user>" по этапно
        for (TiXmlElement *user = database->FirstChildElement("user"); user; user = user->NextSiblingElement("user"))
        {
            // Получаем name
            const char *name = user->Attribute("name");
            if (name)
            {
                printf_wchar(name, L"Name", true);
            }

            // Загружаем категорию "<userdata>"
            TiXmlElement *userdata = user->FirstChildElement("userdata");
            if (userdata)
            {
                // Получаем address
                const char *address = userdata->Attribute("address");
                if (address)
                {
                    printf_wchar(address, L"\tAddress", false);
                }

                // Получаем telephon
                const char *telephon = userdata->Attribute("telephone");
                if (telephon)
                {
                    printf_wchar(telephon, L"\tTelephone", true);
                }
            }

            // Загружаем категорию "<base>"
            TiXmlElement *base = user->FirstChildElement("base");
            if (base)
            {
                wprintf(L"\tBase:\n");

                // Загружаем категорию "<day>" по этапно
                for (TiXmlElement *day = base->FirstChildElement("day"); day; day = day->NextSiblingElement("day"))
                {
                    // Получаем date
                    const char *date = day->Attribute("date");
                    if (date)
                    {
                        printf_wchar(date, L"\t\tDate", false);
                    }

                    // Получаем money
                    const char *money = day->Attribute("money");
                    if (money)
                    {
                        printf_wchar(money, L"\t\tMoney", true);
                    }
                }
            }

            wprintf(L"\n");
        }
    }
    return 0;
}


Результат:

Код:
Name: "Вася Пупкин"
	Address: "Pushkin st. 26"	Telephone: "+123123343"
	Base:
		Date: "25/03/11"		Money: "5000"
		Date: "25/04/11"		Money: "5001"
		Date: "25/05/11"		Money: "5002"
		Date: "25/06/11"		Money: "5003"

Name: "Серёжа (!) Пупкин"
	Address: "Pushkin st. 27"	Telephone: "+123123343"
	Base:
		Date: "25/03/12"		Money: "5004"
		Date: "25/04/12"		Money: "5005"
		Date: "25/05/12"		Money: "5006"
		Date: "25/06/12"		Money: "5007"


Добавлено через 49 минут
Вот пример, в котором собрано всё. Перевод строк с помощью mbstowcs и MultiByteToWideChar. Изменение можно сделать за комментировать или раз комментировать строчку "#define WINDOWS_TRANS".
Пример кода. Используется динамический вид:

Код:
#include "tinyxml/tinystr.h"
#include "tinyxml/tinystr.cpp"
#include "tinyxml/tinyxml.h"
#include "tinyxml/tinyxml.cpp"
#include "tinyxml/tinyxmlerror.cpp"
#include "tinyxml/tinyxmlparser.cpp"

#define WINDOWS_TRANS

#include <locale.h>
#include <stdio.h>
#include <wchar.h>

#ifdef WINDOWS_TRANS
#include <windows.h>

// Функция перевода из char* в wchar_t*
void printf_wchar(const char *mb, const wchar_t *title, bool endOfLine)
{
    wchar_t *wcs = NULL;

    // Получаем необходимую длину для wchar_t*
    int requiredSize = MultiByteToWideChar(CP_UTF8, 0, mb, -1, NULL, 0);
    wcs = new wchar_t[requiredSize];

    wprintf(title);

    if (MultiByteToWideChar(CP_UTF8, 0, mb, -1, wcs, requiredSize)) // Пытаемся ее перекодировать
    {
        // Удачно :) выводим ее и освобождаем память
        wprintf(L": \"%ls\"", wcs);
        delete[] wcs;
    }
    else
    {
        // Неудачно :( Сообщаем об ошибке
        wprintf(L": \"%ls\"", L"не смог перевести в wchar_t*");
    }

    if (endOfLine)
    {
        wprintf(L"\n");
    }
}
#else

// Функция перевода из char* в wchar_t*
void printf_wchar(const char *mb, const wchar_t *title, bool endOfLine)
{
    wchar_t *wcs = NULL;

    // Получаем необходимую длину для wchar_t*
    size_t requiredSize = mbstowcs(NULL, mb, 0);
    wcs = new wchar_t[requiredSize + 1];

    wprintf(title);

    if (mbstowcs(wcs, mb, requiredSize + 1) != size_t(-1)) // Пытаемся ее перекодировать
    {
        // Удачно :) выводим ее и освобождаем память
        wprintf(L": \"%ls\"", wcs);
        delete[] wcs;
    }
    else
    {
        // Неудачно :( Сообщаем об ошибке
        wprintf(L": \"%ls\"", L"не смог перевести в wchar_t*");
    }

    if (endOfLine)
    {
        wprintf(L"\n");
    }
}
#endif

int main()
{
#ifndef WINDOWS_TRANS
    // Устанавливает кодировку UTF-8
    setlocale(LC_ALL, "ru_RU.utf8");
#endif

    // Загружаем xml с кодировкой UTF-8
    TiXmlDocument document("database.xml");
    document.LoadFile(TIXML_ENCODING_UTF8);

    // Загружаем категорию "<database>"
    TiXmlElement *database = document.FirstChildElement("database");
    if (database)
    {
        // Загружаем категорию "<user>" по этапно
        for (TiXmlElement *user = database->FirstChildElement("user"); user; user = user->NextSiblingElement("user"))
        {
            // Получаем name
            const char *name = user->Attribute("name");
            if (name)
            {
                printf_wchar(name, L"Name", true);
            }

            // Загружаем категорию "<userdata>"
            TiXmlElement *userdata = user->FirstChildElement("userdata");
            if (userdata)
            {
                // Получаем address
                const char *address = userdata->Attribute("address");
                if (address)
                {
                    printf_wchar(address, L"\tAddress", false);
                }

                // Получаем telephon
                const char *telephon = userdata->Attribute("telephone");
                if (telephon)
                {
                    printf_wchar(telephon, L"\tTelephone", true);
                }
            }

            // Загружаем категорию "<base>"
            TiXmlElement *base = user->FirstChildElement("base");
            if (base)
            {
                wprintf(L"\tBase:\n");

                // Загружаем категорию "<day>" по этапно
                for (TiXmlElement *day = base->FirstChildElement("day"); day; day = day->NextSiblingElement("day"))
                {
                    // Получаем date
                    const char *date = day->Attribute("date");
                    if (date)
                    {
                        printf_wchar(date, L"\t\tDate", false);
                    }

                    // Получаем money
                    const char *money = day->Attribute("money");
                    if (money)
                    {
                        printf_wchar(money, L"\t\tMoney", true);
                    }
                }
            }

            wprintf(L"\n");
        }
    }
    return 0;
}


Помог, скажи спасибо
L.E.O. вне форума Отправить личное сообщение для L.E.O.
Вверх
Ответить с цитированием
Эти 2 пользователя(ей) сказали cпасибо за это полезное сообщение:
Exodus (04.04.2011), henrypp (07.04.2011)
Старый Добавлено: 07.04.2011, 18:48
  (#13)
henrypp
old-school
Пользователь
 
Аватар для henrypp

По умолчанию

А как создать такую таблицу?

Код:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<database>
<user name="Серёжа Пупкин">
<address>Pushkin st. 27</address>
<telephone>000000</telephone>

<base>
<day date="25/03/12" money="5004"/>
<day date="25/03/12" money="5004"/>
</base>
</user>
</database>
Как проверить наличие где user/name = "Какое нибудь имя"? Циклом проходить?

И как менять данные в подячейках? (base)


www.henrypp.org | github.com/henrypp | instagr.am/penmyak
henrypp вне форума Отправить личное сообщение для henrypp
Вверх
Ответить с цитированием
Старый Добавлено: 08.04.2011, 15:40
  (#14)
L.E.O.
Пользователь
Пользователь
 
Аватар для L.E.O.

По умолчанию

Цитата:
Сообщение от henrypp Посмотреть сообщение
Как проверить наличие где user/name = "Какое нибудь имя"? Циклом проходить?

Да, только так. В TinyXML, я по крайней мере, не нашел написанного поиска. В коде, который будет ниже, этот поиск написан (поиск по имени). Использовать его можно так (в примере ищется "Вася Пупкин"):
Пример кода:

Код:
    wchar_t *name = get_wchar("Вася Пупкин");

    Vasja = document.user(name);
    if (Vasja)
    {
        std::cwout << L"Вася найден" << std::endl;
    }
    else
    {
        std::cwout << L"Вася НЕ найден" << std::endl;
    }
    delete[] name;


Цитата:
Сообщение от henrypp Посмотреть сообщение
А как создать такую таблицу?

Цитата:
Сообщение от henrypp Посмотреть сообщение
И как менять данные в подячейках? (base)

Вот пример кода и класса обработки такой таблицы:

Код:
#include "tinyxml/tinystr.h"
#include "tinyxml/tinystr.cpp"
#include "tinyxml/tinyxml.h"
#include "tinyxml/tinyxml.cpp"
#include "tinyxml/tinyxmlerror.cpp"
#include "tinyxml/tinyxmlparser.cpp"

#include <locale.h>
#include <wchar.h>
#include <iostream>
#include <list>
#include <map>
#include <algorithm>

/*
------------------------------------------------------------------
   DatabaseBase
------------------------------------------------------------------
*/

typedef std::pair<const char*, const char*> DatabaseBase;
typedef std::pair<wchar_t*, wchar_t*> DatabaseBaseW;

inline void deleteDatabaseBaseW(DatabaseBaseW &base)
{
    delete[] base.first;
    delete[] base.second;
}

/*
------------------------------------------------------------------
   Функции перевода из char* в wchar_t*
------------------------------------------------------------------
*/
inline wchar_t* get_wchar(const char *mb)
{
    wchar_t *wcs = NULL;

    // Получаем необходимую длину для wchar_t*
    size_t requiredSize = mbstowcs(NULL, mb, 0);
    wcs = new wchar_t[requiredSize + 1];

    if (mbstowcs(wcs, mb, requiredSize + 1) == size_t(-1)) // Пытаемся ее перекодировать
    {
        // Неудачно :(
        delete[] wcs;
        wcs = 0;
    }
    return wcs;
}

inline void setValueWChar(wchar_t* &wcs, const char *mb)
{
    if (wcs)
    {
        delete[] wcs;
    }
    wcs = get_wchar(mb);
}

/*
------------------------------------------------------------------
   DatabaseUser
------------------------------------------------------------------
*/

class DatabaseUser
{
    friend class DatabaseDocument;

public:
    DatabaseUser(TiXmlElement *user);
    ~DatabaseUser();

    const char *name() const;
    inline const wchar_t *wname() const { return (m_user) ? m_name : NULL; }

    const char *address() const;
    inline const wchar_t *waddress() const { return (m_user) ? m_address : NULL; }
    void setAddress(const char *addressValue);

    const char *telephon() const;
    inline const wchar_t *wtelephon() const { return (m_user) ? m_telephon : NULL; }
    void setTelephon(const char *telephonValue);

    inline size_t countBase() const { return m_base.size(); }

    DatabaseBase base(int index) const;
    DatabaseBaseW wbase(int index) const;
    void setBase(int index, const char *dateValue, const char *moneyValue);

    const char *date(int index) const;
    const wchar_t *wdate(int index) const;
    void setDate(int index, const char *dateValue);

    const char *money(int index) const;
    const wchar_t *wmoney(int index) const;
    void setMoney(int index, const char *moneyValue);

    inline friend std::wostream& operator << (std::wostream &wcout, const DatabaseUser &user);

private:
    DatabaseBaseW& m_wbase(int index);
    void setName(const char *nameValue);

    TiXmlElement *m_user;

    wchar_t *m_name;
    wchar_t *m_address;
    wchar_t *m_telephon;

    std::list<DatabaseBaseW> m_base;
};

std::wostream& operator << (std::wostream &wcout, const DatabaseUser &user)
{
    wcout << L"Name: " << user.m_name << std::endl;
    wcout << L"\tAddress: " << user.m_address << std::endl;
    wcout << L"\tTelephon: " << user.m_telephon << std::endl;
    wcout << L"\tBase (count = " << user.countBase() << L"):" << std::endl;

    size_t index = 0;
    for (std::list<DatabaseBaseW>::const_iterator i = user.m_base.begin(); i != user.m_base.end(); ++i)
    {
        ++index;
        wcout << L"\t\t" << index << L") Date: " << i->first << L"\tMoney: " << i->second << std::endl;
    }

    return wcout;
}

DatabaseUser::DatabaseUser(TiXmlElement *user) :
    m_user(user)
{
    if (!m_user)
    {
        return;
    }

    // Получаем name и переводим в UTF-16
    const char *userName = name();
    m_name = (userName) ? get_wchar(userName) : NULL;

    // Получаем address и переводим в UTF-16
    const char *addressValue = address();
    m_address = (addressValue) ? get_wchar(addressValue) : NULL;

    // Получаем telephone и переводим в UTF-16
    const char *telephonValue = telephon();
    m_telephon = (telephonValue) ? get_wchar(telephonValue) : NULL;

    // Загружаем категорию "<base>"
    const TiXmlElement *base = user->FirstChildElement("base");
    if (base)
    {
        // Загружаем категорию "<day>" по этапно
        for (const TiXmlElement *dayElement = base->FirstChildElement("day"); dayElement; dayElement = dayElement->NextSiblingElement("day"))
        {
            // Получаем date и переводим в UTF-16
            const char *dateValue = dayElement->Attribute("date");
            wchar_t *wdate = (dateValue) ? get_wchar(dateValue) : NULL;

            // Получаем money и переводим в UTF-16
            const char *moneyValue = dayElement->Attribute("money");
            wchar_t *wmoney = (moneyValue) ? get_wchar(moneyValue) : NULL;

            m_base.push_back(std::make_pair(wdate, wmoney));
        }
    }
}

DatabaseUser::~DatabaseUser()
{
    if (m_name)
    {
        delete[] m_name;
    }

    if (m_address)
    {
        delete[] m_address;
    }

    if (m_telephon)
    {
        delete[] m_telephon;
    }

    std::for_each(m_base.begin(), m_base.end(), deleteDatabaseBaseW);
}

const char *DatabaseUser::name() const
{
    return (m_user) ? m_user->Attribute("name") : NULL;
}

void DatabaseUser::setName(const char *nameValue)
{
    if (m_user)
    {
        // Устанавливаем в документе
        m_user->SetAttribute("name", nameValue);

        // Переводим его в UTF-16
        setValueWChar(m_name, nameValue);
    }
}

const char *DatabaseUser::address() const
{
    if (m_user)
    {
        // Получаем address через класс TiXmlHandle
        const TiXmlHandle userHandle(m_user);
        const TiXmlText *addressText = userHandle.FirstChild("address").FirstChild().ToText();

        if (addressText)
        {
            return addressText->Value();
        }
    }
    return NULL;
}

void DatabaseUser::setAddress(const char *addressValue)
{
    if (m_user)
    {
        // Получаем address через класс TiXmlHandle
        TiXmlHandle userHandle(m_user);
        TiXmlText *addressText = userHandle.FirstChild("address").FirstChild().ToText();

        // Устанавливаем в документе
        addressText->SetValue(addressValue);

        // Переводим его в UTF-16
        setValueWChar(m_address, addressValue);
    }
}

const char *DatabaseUser::telephon() const
{
    if (m_user)
    {
        // Получаем telephone через класс TiXmlHandle
        const TiXmlHandle userHandle(m_user);
        const TiXmlText *telephonText = userHandle.FirstChild("telephone").FirstChild().ToText();

        if (telephonText)
        {
            return telephonText->Value();
        }
    }
    return NULL;
}

void DatabaseUser::setTelephon(const char *telephonValue)
{
    if (m_user)
    {
        // Получаем address через класс TiXmlHandle
        TiXmlHandle userHandle(m_user);
        TiXmlText *telephonText = userHandle.FirstChild("telephone").FirstChild().ToText();

        // Устанавливаем в документе
        telephonText->SetValue(telephonValue);

        // Переводим его в UTF-16
        setValueWChar(m_telephon, telephonValue);
    }
}

DatabaseBase DatabaseUser::base(int index) const
{
    if (m_user)
    {
        // Загружаем нужный day через класс TiXmlHandle
        const TiXmlHandle userHandle(m_user);
        const TiXmlElement *dayElement = userHandle.FirstChild("base").Child("day", index).ToElement();

        if (dayElement)
        {
            // Получаем date и money
            return std::make_pair(dayElement->Attribute("date"),
                                  dayElement->Attribute("money"));
        }
    }
    return std::make_pair((const char*)NULL, (const char*)NULL);
}

DatabaseBaseW DatabaseUser::wbase(int index) const
{
    std::list<DatabaseBaseW>::const_iterator i = m_base.begin();
    while (index && i != m_base.end())
    {
        --index;
        ++i;
    }
    return (i == m_base.end()) ? std::make_pair((wchar_t*)NULL, (wchar_t*)NULL) : *i;
}

DatabaseBaseW& DatabaseUser::m_wbase(int index)
{
    std::list<DatabaseBaseW>::iterator i = m_base.begin();
    while (index)
    {
        --index;
        ++i;
    }
    return (*i);
}

void DatabaseUser::setBase(int index, const char *dateValue, const char *moneyValue)
{
    if (m_user)
    {
        // Загружаем нужный day через класс TiXmlHandle
        TiXmlHandle userHandle(m_user);
        TiXmlElement *dayElement = userHandle.FirstChild("base").Child("day", index).ToElement();

        if (dayElement)
        {
            // Устанавливаем в документе
            dayElement->SetAttribute("date", dateValue);
            dayElement->SetAttribute("money", moneyValue);

            // Переводим его в UTF-16
            DatabaseBaseW &base = m_wbase(index);
            setValueWChar(base.first, dateValue);
            setValueWChar(base.second, moneyValue);
        }
    }
}

const char *DatabaseUser::date(int index) const
{
    if (m_user)
    {
        // Загружаем нужный day через класс TiXmlHandle
        const TiXmlHandle userHandle(m_user);
        const TiXmlElement *dayElement = userHandle.FirstChild("base").Child("day", index).ToElement();

        // Получаем date
        if (dayElement)
        {
            return dayElement->Attribute("date");
        }
    }
    return NULL;
}

const wchar_t *DatabaseUser::wdate(int index) const
{
    return wbase(index).first;
}

void DatabaseUser::setDate(int index, const char *dateValue)
{
    if (m_user)
    {
        // Загружаем нужный day через класс TiXmlHandle
        TiXmlHandle userHandle(m_user);
        TiXmlElement *dayElement = userHandle.FirstChild("base").Child("day", index).ToElement();

        if (dayElement)
        {
            // Устанавливаем в документе
            dayElement->SetAttribute("date", dateValue);

            // Переводим его в UTF-16
            DatabaseBaseW &base = m_wbase(index);
            setValueWChar(base.first, dateValue);
        }
    }
}

const char *DatabaseUser::money(int index) const
{
    if (m_user)
    {
        // Загружаем нужный day через класс TiXmlHandle
        const TiXmlHandle userHandle(m_user);
        const TiXmlElement *dayElement = userHandle.FirstChild("base").Child("day", index).ToElement();

        // Получаем money
        if (dayElement)
        {
            return dayElement->Attribute("money");
        }
    }
    return NULL;
}

const wchar_t *DatabaseUser::wmoney(int index) const
{
    return wbase(index).second;
}

void DatabaseUser::setMoney(int index, const char *moneyValue)
{
    if (m_user)
    {
        // Загружаем нужный day через класс TiXmlHandle
        TiXmlHandle userHandle(m_user);
        TiXmlElement *dayElement = userHandle.FirstChild("base").Child("day", index).ToElement();

        if (dayElement)
        {
            // Устанавливаем в документе
            dayElement->SetAttribute("money", moneyValue);

            // Переводим его в UTF-16
            DatabaseBaseW &base = m_wbase(index);
            setValueWChar(base.second, moneyValue);
        }
    }
}

/*
------------------------------------------------------------------
   DatabaseDocument
------------------------------------------------------------------
*/

struct strless
{
    bool operator()(const wchar_t *a, const wchar_t *b) const
    {
        return wcscmp(a, b) < 0;
    }
};

typedef std::map<const wchar_t*, DatabaseUser*, strless> UserMap;

class DatabaseDocument
{
public:
    DatabaseDocument(const char *documentName, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
    ~DatabaseDocument();

    bool saveFile() const;
    bool saveFile(const char *documentName) const;

    inline size_t countUser() const { return m_users.size(); }
    DatabaseUser *user(const wchar_t *name) const;
    void setNameUser(const wchar_t *oldName, const char *newName);

    inline friend std::wostream& operator << (std::wostream &wcout, const DatabaseDocument &doc);

private:
    TiXmlDocument document;
    UserMap m_users;
};

std::wostream& operator << (std::wostream &wcout, const DatabaseDocument &doc)
{
    for (std::map<const wchar_t*, DatabaseUser*>::const_iterator i = doc.m_users.begin(); i != doc.m_users.end(); ++i)
    {
        wcout << (*i->second) << std::endl;
    }
    return wcout;
}

DatabaseDocument::DatabaseDocument(const char *documentName, TiXmlEncoding encoding)
{
    if (document.LoadFile(documentName, encoding))
    {
        // Загружаем категорию "<database>"
        TiXmlElement *database = document.FirstChildElement("database");
        if (database)
        {
            // Загружаем категорию "<user>" по этапно
            for (TiXmlElement *user = database->FirstChildElement("user"); user; user = user->NextSiblingElement("user"))
            {
                DatabaseUser *dbUser = new DatabaseUser(user);
                m_users[dbUser->wname()] = dbUser;
            }
        }
    }
}

DatabaseDocument::~DatabaseDocument()
{
    for (UserMap::iterator i = m_users.begin(); i != m_users.end(); ++i)
    {
        delete i->second;
    }
}

bool DatabaseDocument::saveFile() const
{
    return document.SaveFile();
}

bool DatabaseDocument::saveFile(const char *documentName) const
{
    return document.SaveFile(documentName);
}

DatabaseUser *DatabaseDocument::user(const wchar_t *name) const
{
    UserMap::const_iterator i = m_users.find(name);
    return (i != m_users.end()) ? i->second : NULL;
}

void DatabaseDocument::setNameUser(const wchar_t *oldName, const char *newName)
{
    UserMap::iterator i = m_users.find(oldName);
    if (i != m_users.end())
    {
        DatabaseUser *m_user = i->second;
        m_user->setName(newName);

        m_users.erase(i);
        m_users[m_user->wname()] = m_user;
    }
}

/*
------------------------------------------------------------------
   Main
------------------------------------------------------------------
*/

int main()
{
    // Устанавливает кодировку UTF-8
    setlocale(LC_ALL, "ru_RU.utf8");

    // Загружаем xml с кодировкой UTF-8
    DatabaseDocument document("database.xml", TIXML_ENCODING_UTF8);
    std::wcout << L"First load:\n" << document << std::endl;

    DatabaseUser *Vasja;

    wchar_t *name = get_wchar("Вася Пупкин");

    Vasja = document.user(name);
    if (Vasja)
    {
        Vasja->setMoney(1, "20001");
    }
    std::wcout << L"First change document:\n" << document << std::endl;

    document.setNameUser(name, "Ялутровс Вася Пупкин");
    std::wcout << L"Second change document:\n" << document << std::endl;

    setValueWChar(name, "Ялутровс Вася Пупкин");
    Vasja = document.user(name);
    if (Vasja)
    {
        Vasja->setTelephon("x-xxx-xx-xx-xxx");
        Vasja->setDate(3, "25/05/12");
    }
    std::wcout << L"Third change document:\n" << document << std::endl;

    document.saveFile("new_database.xml");

    delete[] name;

    return 0;
}
Замечания:
  1. Код был написан на скорую руку, так что в нем могут быть ошибки.
  2. При поиске user по имени, используется стандартное сравнение строк, так что и кодировки должны быть одинаковыми. Для этого используем переводы в другие кодировки, например мной написанный get_wchar.
  3. get_wchar не очищает память возвращаемой строки, за исключением случаю, когда функция не смогла перекодировать строку!!! Так что вы должны сами очищать память.
  4. Перекодирование строк с использованием MultiByteToWideChar здесь я удалил, так как эта функция не кросс платформенная. Пример перевода с этой функцией, вы сможете найти в пред предыдущем посте.
  5. По хорошему, классы нужно перекинуть по разным файлам. Но так сложнее было бы мне продемонстрировать код, поэтому я собрал все в одну кучу.


Описание классов:

Класс DatabaseBase:
на самом деле это просто std::pair, в котором хранится две строки: в первой (first) хранится запись date из категории "base"; во второй (second) хранится запись money из категории "base". Строки хранятся так, как возвращает библиотека TinyXML.

Класс DatabaseBaseW:
Тоже самое, что и DatabaseBase, только хранит строки в формате UTF-16.

Класс DatabaseUser:
const char *name() - возвращает имя данного user
const wchar_t *wname() - возвращает имя данного user в UTF-16

const char *address() - возвращает адрес данного user
const wchar_t *waddress() - возвращает адрес данного user в UTF-16
void setAddress(const char *addressValue) - меняет адрес данного user на addressValue

const char *telephon() - возвращает телефон данного user
const wchar_t *wtelephon() - возвращает телефон данного user в UTF-16
void setTelephon(const char *telephonValue) - меняет телефон данного user на telephonValue

size_t countBase() - возвращает кол-во записей категории "base".

DatabaseBase base(int index) - возвращает DatabaseBase с порядковым номером index (нумерация начинается с нуля)
DatabaseBaseW wbase(int index) - возвращает DatabaseBaseW с порядковым номером index (нумерация начинается с нуля)
void setBase(int index, const char *dateValue, const char *moneyValue) - меняет дату и деньги данного user на dateValue и moneyValue соотв. в записи категории "base", порядковый номер которого равен index (нумерация начинается с нуля)

const char *date(int index) - возвращает дату с порядковым номером index (нумерация начинается с нуля)
const wchar_t *wdate(int index) - возвращает дату с порядковым номером index (нумерация начинается с нуля) в UTF-16
void setDate(int index, const char *dateValue) - меняет дату данного user на dateValue в записи категории "base", порядковый номер которого равен index (нумерация начинается с нуля)

const char *money(int index) - возвращает деньги с порядковым номером index (нумерация начинается с нуля)
const wchar_t *wmoney(int index) - возвращает деньги с порядковым номером index (нумерация начинается с нуля) в UTF-16
void setMoney(int index, const char *moneyValue) - меняет деньги данного user на moneyValue в записи категории "base", порядковый номер которого равен index (нумерация начинается с нуля)

Класс DatabaseDocument:
DatabaseDocument(const char *documentName, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING) - создание класса с параметрами:
documentName - имя открываемого XML файла.
encoding - в какой кодировке открыть. Значение кодировок хранятся в библиотеке TinyXML.

bool saveFile() - сохраняет XML файл под тем же именем
bool saveFile(const char *documentName) - сохраняет XML файл под именем documentName

size_t countUser() - кол-во записей "user"
DatabaseUser *user(const wchar_t *name) - возвращает всю запись "user", у которого имя name (см. замечание 2 в пункте "Вот пример кода и класса обработки такой таблицы")
void setNameUser(const wchar_t *oldName, const char *newName) - меняет имя у записи "user", имя которго oldName, на новое имя newName (см. замечание 2 в пункте "Вот пример кода и класса обработки такой таблицы").



Обрабатываемый XML:

Код:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<database>
	<user name="Серёжа Пупкин">
		<address>Pushkin st. 27</address>
		<telephone>000000</telephone>

		<base>
			<day date="25/03/12" money="5004"/>
			<day date="25/04/12" money="5005"/>
		</base>
	</user>

	<user name="Вася Пупкин">
		<address>Pushkin st. 26</address>
		<telephone>+123123343</telephone>

		<base>
		  <day date="25/03/11" money="5000"/>
		  <day date="25/04/11" money="5001"/>
		  <day date="25/05/11" money="5002"/>
		  <day date="25/06/11" money="5003"/>
		</base>
	</user>

	<user name="Серёжа (!) Пупкин">
		<address>Pushkin st. 28</address>
		<telephone>+123123343</telephone>

		<base>
		  <day date="25/01/12" money="6000"/>
		  <day date="25/02/12" money="7000"/>
		  <day date="25/03/12" money="8000"/>
		  <day date="25/04/12" money="9000"/>
		</base>
	</user>
</database>


XML файл на выходе с измененными данными:

Код:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<database>
    <user name="Серёжа Пупкин">
        <address>Pushkin st. 27</address>
        <telephone>000000</telephone>
        <base>
            <day date="25/03/12" money="5004" />
            <day date="25/04/12" money="5005" />
        </base>
    </user>
    <user name="Ялутровс Вася Пупкин">
        <address>Pushkin st. 26</address>
        <telephone>x-xxx-xx-xx-xxx</telephone>
        <base>
            <day date="25/03/11" money="5000" />
            <day date="25/04/11" money="20001" />
            <day date="25/05/11" money="5002" />
            <day date="25/05/12" money="5003" />
        </base>
    </user>
    <user name="Серёжа (!) Пупкин">
        <address>Pushkin st. 28</address>
        <telephone>+123123343</telephone>
        <base>
            <day date="25/01/12" money="6000" />
            <day date="25/02/12" money="7000" />
            <day date="25/03/12" money="8000" />
            <day date="25/04/12" money="9000" />
        </base>
    </user>
</database>


И результат вывода:

First load:
Name: Вася Пупкин
Address: Pushkin st. 26
Telephon: +123123343
Base (count = 4):
1) Date: 25/03/11 Money: 5000
2) Date: 25/04/11 Money: 5001
3) Date: 25/05/11 Money: 5002
4) Date: 25/06/11 Money: 5003

Name: Серёжа (!) Пупкин
Address: Pushkin st. 28
Telephon: +123123343
Base (count = 4):
1) Date: 25/01/12 Money: 6000
2) Date: 25/02/12 Money: 7000
3) Date: 25/03/12 Money: 8000
4) Date: 25/04/12 Money: 9000

Name: Серёжа Пупкин
Address: Pushkin st. 27
Telephon: 000000
Base (count = 2):
1) Date: 25/03/12 Money: 5004
2) Date: 25/04/12 Money: 5005


First change document:
Name: Вася Пупкин
Address: Pushkin st. 26
Telephon: +123123343
Base (count = 4):
1) Date: 25/03/11 Money: 5000
2) Date: 25/04/11 Money: 20001
3) Date: 25/05/11 Money: 5002
4) Date: 25/06/11 Money: 5003

Name: Серёжа (!) Пупкин
Address: Pushkin st. 28
Telephon: +123123343
Base (count = 4):
1) Date: 25/01/12 Money: 6000
2) Date: 25/02/12 Money: 7000
3) Date: 25/03/12 Money: 8000
4) Date: 25/04/12 Money: 9000

Name: Серёжа Пупкин
Address: Pushkin st. 27
Telephon: 000000
Base (count = 2):
1) Date: 25/03/12 Money: 5004
2) Date: 25/04/12 Money: 5005


Second change document:
Name: Серёжа (!) Пупкин
Address: Pushkin st. 28
Telephon: +123123343
Base (count = 4):
1) Date: 25/01/12 Money: 6000
2) Date: 25/02/12 Money: 7000
3) Date: 25/03/12 Money: 8000
4) Date: 25/04/12 Money: 9000

Name: Серёжа Пупкин
Address: Pushkin st. 27
Telephon: 000000
Base (count = 2):
1) Date: 25/03/12 Money: 5004
2) Date: 25/04/12 Money: 5005

Name: Ялутровс Вася Пупкин
Address: Pushkin st. 26
Telephon: +123123343
Base (count = 4):
1) Date: 25/03/11 Money: 5000
2) Date: 25/04/11 Money: 20001
3) Date: 25/05/11 Money: 5002
4) Date: 25/06/11 Money: 5003


Third change document:
Name: Серёжа (!) Пупкин
Address: Pushkin st. 28
Telephon: +123123343
Base (count = 4):
1) Date: 25/01/12 Money: 6000
2) Date: 25/02/12 Money: 7000
3) Date: 25/03/12 Money: 8000
4) Date: 25/04/12 Money: 9000

Name: Серёжа Пупкин
Address: Pushkin st. 27
Telephon: 000000
Base (count = 2):
1) Date: 25/03/12 Money: 5004
2) Date: 25/04/12 Money: 5005

Name: Ялутровс Вася Пупкин
Address: Pushkin st. 26
Telephon: x-xxx-xx-xx-xxx
Base (count = 4):
1) Date: 25/03/11 Money: 5000
2) Date: 25/04/11 Money: 20001
3) Date: 25/05/11 Money: 5002
4) Date: 25/05/12 Money: 5003




Помог, скажи спасибо
L.E.O. вне форума Отправить личное сообщение для L.E.O.
Вверх
Ответить с цитированием
Этот пользователь сказал cпасибо за это полезное сообщение:
henrypp (08.04.2011)
Старый Добавлено: 29.06.2011, 12:38
  (#15)
henrypp
old-school
Пользователь
 
Аватар для henrypp

По умолчанию

Спасибо всем кто помог, но TinyXML это никак не Tiny (скорее Biggy)

Решил задачу давно с помощью pugixml.

Пример выводящий имена клиентов
Код:
xml_document doc;
xml_node database;

doc.load_file(L"database.xml");

database = doc.child(L"database");

for(xml_node item = doc.child(L"user"); item; item = item.next_sibling(L"user"))
	wcout << item.append_attribute(L"name") << endl; // Выводим имя клиента
имеется встроенная конверсия в wchar_t (не нужно никаких mbtowc), нужно всего лишь объявить PUGIXML_WCHAR_MODE

Здесь последняя версия исходников:
[Ссылки могут видеть только зарегистрированные пользователи. ]


www.henrypp.org | github.com/henrypp | instagr.am/penmyak

Последний раз редактировалось henrypp; 29.06.2011 в 12:44..
henrypp вне форума Отправить личное сообщение для henrypp
Вверх
Ответить с цитированием
Ответ

Опции темы
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.10
Copyright ©2000 - 2019, vBulletin Solutions, Inc. Перевод: zCarot

Время генерации страницы 0.17995 секунды с 16 запросами