Russian Qt Forum

Qt => XML => Тема начата: sergek от Январь 06, 2010, 17:10



Название: [решено] Чтение XML, запись XML
Отправлено: sergek от Январь 06, 2010, 17:10
Коллеги!
Подскажите, пожалуйста, правильно ли я понял - при чтении и обработке содержимого xml-файлов большого размера (сотни кбайт - десятки Мбайт) средствами Qt следует использовать QXmlSimpleReader, для записи результатов в xml-файл - QXmlStreamWriter?

DOM прошу не предлагать, не подходит. С SAX понятно - для чтения, а вот примеров создания xml-файлов нашел немного. Особенно порадовал SAX Bookmarks Example :(
Спасибо.


Название: Re: Чтение XML, запись XML
Отправлено: Akaiten от Январь 15, 2010, 15:46
Для чтения использую QXmlStreamReader, для создания QXmlStreamWriter. А что с созданием непонятно? это ж проще пареной репы. Например

Код
C++ (Qt)
QXmlStreamWriter xml;
/*...*/
xml.writeStartElement("a");
xml.writeAttribute("href", "#");
xml.writeStartElement("img");
xml.writeAttribute("src", "image.jpg");
xml.writeAttribute("alt", "image");
xml.writeEndElement();
xml.writeCharacters("This is a link");
xml.writeEndElement();
xml.writeTextElement("p", "This is a paragraph");
 

Должно получиться что то вроде этого
Код
XML
<a href="#">
 <img src="image.jpg" alt="image"/>
 This is a link
</a>
<p>This is a paragraph</p>
 

Добавлено 18.01.2010 11:14: Забыл закрывающий тег для <img> :)


Название: Re: Чтение XML, запись XML
Отправлено: Marat(Qt) от Январь 16, 2010, 16:29
Должно получиться что то вроде этого
Код
XML
<a href="#">
 <img src="image.jpg" alt="image">
 This is a link
</a>
<p>This is a paragraph</p>
 
Тэг img не закрыт


Название: Re: Чтение XML, запись XML
Отправлено: Resager от Январь 16, 2010, 22:02
Тэг img не нуждается в закрытии, однако по стандарту XHTML 1.0 нужно дописывать (для такого типа тэгов) в конце "/"

Как то: <img src="image.jpg" alt="image" />
(читать здесь http://stepbystep.htmlbook.ru/?id=63)

Неужели там это непридусмотрено? :(

Мне как раз понадобится работа с XML файлами... не подскажете ли (пару примерчиков) чтения XML-файла?
Меня особенно интересует, можно ли "добавлять" в XML, в середину, текст большей размерности
(буду использовать по прототипу БД, для хранения многострочных текстовых данных), не использую нормальные БД по причине того. что поле ограниченной длины.
Например было:
Код
XML
<root24>
  <group1>
     <string1 key="comment" value="string1"/>
     <string2 key="comment" value="string2"/>
  </group1>
</root24>

Стало:

Код
XML
<root24>
  <group1>
     <string1 key="comment" value="string1"/>
     <string2 key="comment" value="string2"/>
     <string3 key="comment" value="string3"/>
  </group1>
</root24>
(
Ну и конечно чтобы это быстро было.


Название: Re: Чтение XML, запись XML
Отправлено: Marat(Qt) от Январь 16, 2010, 23:09
Если положите это в value=... (т.е. в атрибут), то наткнетесь на ту же проблему что и в соседнем топике http://www.prog.org.ru/topic_11648_0.html (http://www.prog.org.ru/topic_11648_0.html)
Если будете активно DOM использовать - то вся база будет в ОЗУ лежать, а так вполне ничего. Жить можно будет.


Название: Re: Чтение XML, запись XML
Отправлено: Resager от Январь 17, 2010, 13:02
Есть хорошие примеры для работы с DOM? Это хорошо, с одной стороны, что хранится будет в ОЗУ, и быстро, и как вы говорите проблем меньше, а если мне не охота нагружать ОЗУ этив файлом, ибо предпологается большой размер файла (~100 мб максимум). Думаю надо проверить на быстродействие оба варианта, и если видимого отличия не будет, тогда лучше не занимать память.
Класть мне всё равно куда, хотя в "KEY", хоть вообще между тэгами (ибо придётся как уже говорил как то многострочные текстовые блоки хранить).
Так сможете подсказать?

З.Ы. В примере выше не показано, как например подключать файл, если не сложно. покажите?!


Название: Re: Чтение XML, запись XML
Отправлено: sergek от Январь 17, 2010, 14:30
Для чтения использую QXmlStreamReader, для создания QXmlStreamWriter. А что с созданием непонятно? это ж проще пареной репы. Например

Код
C++ (Qt)
QXmlStreamWriter xml;
/*...*/
xml.writeStartElement("a");
xml.writeAttribute("href", "#");
xml.writeStartElement("img");
...
 
Я бы не назвал это пареной репой :) Однако ход мысли понятен, спасибо. Предстоит много нудной и противной работы  :(


Название: Re: Чтение XML, запись XML
Отправлено: Marat(Qt) от Январь 17, 2010, 15:11
Есть хорошие примеры для работы с DOM? Это хорошо, с одной стороны, что хранится будет в ОЗУ, и быстро, и как вы говорите проблем меньше, а если мне не охота нагружать ОЗУ этив файлом, ибо предпологается большой размер файла (~100 мб максимум). Думаю надо проверить на быстродействие оба варианта, и если видимого отличия не будет, тогда лучше не занимать память.
Класть мне всё равно куда, хотя в "KEY", хоть вообще между тэгами (ибо придётся как уже говорил как то многострочные текстовые блоки хранить).
Так сможете подсказать?


У xml есть язык запросов - XQuery, не знаю как он для вставки (по сути должны быть средства) но вот для чтения очень даже. Составляете запрос и получаете конкретную строку или набор строк, удовлетворяющих запросу.


Название: Re: Чтение XML, запись XML
Отправлено: Akaiten от Январь 18, 2010, 11:19
Меня особенно интересует, можно ли "добавлять" в XML, в середину, текст большей размерности
...
Ну и конечно чтобы это быстро было.

Обычно, чтобы изменить XML файлик требуеться его полностью прочитать (и обычно распарсить) в память.


Название: Re: Чтение XML, запись XML
Отправлено: Resager от Январь 18, 2010, 16:29
Обычно, чтобы изменить XML файлик требуеться его полностью прочитать (и обычно распарсить) в память.
Ну это логично, по другому думаю никак, даже текстовый файл...
Как происходит загрузка в память XML-файла и его парсинг, что для этого есть в QT?


Название: Re: Чтение XML, запись XML
Отправлено: Marat(Qt) от Январь 18, 2010, 16:50
Обычно, чтобы изменить XML файлик требуеться его полностью прочитать (и обычно распарсить) в память.
Ну это логично, по другому думаю никак, даже текстовый файл...
Как происходит загрузка в память XML-файла и его парсинг, что для этого есть в QT?
Для этого есть DOM. Он загружает весь файл и строит из него дерево, по которому вы можете ходить, читая/добавляя/удаляя ветки. Все это достаточно подробно описал М.Шлее.


Название: Re: Чтение XML, запись XML
Отправлено: Akaiten от Январь 18, 2010, 17:03
Обычно, чтобы изменить XML файлик требуеться его полностью прочитать (и обычно распарсить) в память.
Ну это логично, по другому думаю никак, даже текстовый файл...
Как происходит загрузка в память XML-файла и его парсинг, что для этого есть в QT?

Могу ещё раз написать, что я, например, использую QXmlStreamReader. Если структура XML довольна проста, то при помощи QXmlStreamReader его можно просто и быстро распарсить. В качестве примера смотри Qt\tools\linguist\shared\ts.cpp - парсер TS-файлов. Также для чтения можно использовать QXmlSimpleReader (ни разу не использовал) или QDomDocument.


Название: Re: Чтение XML, запись XML
Отправлено: SABROG от Январь 21, 2010, 10:12
QXmlStreamReader это StAX, по сути может работать с любым объемом, на его основе можно построить сетевой протокол на базе xml запросов и ответов. Т.е. ему всё равно какого размера файл, он будет работать. Из недостатков - только для чтения, только forward cursor, т.е. нельзя вернуться в предыдущую ноду и посмотреть чего там было, код парсера получается некрасивым, не понятным, много оверхеда (повторяющегося кода). Зато хорошо работает.


Название: con: Чтение XML, запись XML
Отправлено: sergek от Январь 28, 2010, 22:07
Коллеги, в продолжение темы.
При записи с помощью QXmlStreamWriter xml-документа нужно, чтобы в декларацию xml записывалась кодировка, типа <?xml version="1.0" encoding="WINDOWS-1251"?>.
Курю assistant, в котором говорится что при использовании QXmlStreamWriter::setCodec ( QTextCodec * codec ) the encoding information is stored in the initial xml tag. Главное, чтобы вызов this function был before calling writeStartDocument().
Пишу
    QXmlStreamWriter writer;
    writer.setCodec("WINDOWS-1251");
    writer.writeStartDocument();
Фигвам. В созданном документе декларация выглядит как <?xml version="1.0"?> без encoding.
Думал, что кодек кривой, так нет - если сделать
    QTextCodec * c=writer.codec();
    QString sc=c->name();
то sc показывает правильно: windows-1251. Пробовал разную кодировку, в том числе UTF, все едино...
Подскажите, как правильно сформировать декларацию в прологе?
Гуру, ау!!!


Название: Re: Чтение XML, запись XML
Отправлено: BRE от Январь 28, 2010, 22:28
Попробуй воспользоваться этим:
void QXmlStreamWriter::writeProcessingInstruction ( const QString & target, const QString & data = QString() )


Название: Re: Чтение XML, запись XML
Отправлено: Akaiten от Январь 29, 2010, 09:46
Encoding записывается только в случае, если QXmlStreamWriter пишет в QIODevice.


Название: Re: Чтение XML, запись XML
Отправлено: SABROG от Январь 29, 2010, 14:11
Создаю xml через QXmlStreamWriter, в качестве устройства передаю QByteArray (он не QIODevice). Без явной установки кодировки получаю "UTF-8", при Windows-1251:

Код
C++ (Qt)
   ...
   xml.setCodec("Windows-1251");
   xml.setAutoFormatting(true);
   xml.writeStartDocument();
   ...
 

Получаю в xml - "Windows-1251". Qt 4.6.1. Всё работает.


Название: Re: Чтение XML, запись XML
Отправлено: sergek от Январь 29, 2010, 20:51
Цитировать
Encoding записывается только в случае, если QXmlStreamWriter пишет в QIODevice
Цитировать
Создаю xml через QXmlStreamWriter, в качестве устройства передаю QByteArray (он не QIODevice)
Вот где собака порылась! Из трех конструкторов QXmlStreamWriter с параметрами QIODevice, QByteArray и QString я выбрал тот, который мне был понятнее (последний). А он-то и работает кривовато. Просто заменил строку     
Код:
    QString outXml;
    QXmlStreamWriter writer(&outXml);
   
на
Код:
    QByteArray outXml;
    QXmlStreamWriter writer(&outXml);
   
и что хотел, получил.
Спасибо!


Название: Re: Чтение XML, запись XML
Отправлено: warlock от Февраль 08, 2010, 00:42
Возможно ли обновлять xml файл без полной его перезаписи? Т.е. обновить какуето запись?


Название: Re: Чтение XML, запись XML
Отправлено: sergek от Февраль 25, 2010, 21:21
  Коллеги, еще вопрос, если позволите.
  Нет ли какой-нибудь особенности преобразования прописной русской буквы "И" из UTF-8 с помощью QTextCodec? Проблема следующая. Имеется файл примерно такой (скопировано из QTextEdit):
Код:
<?xml version="1.0" encoding="UTF-8"?>
<ED Acc="11111��1111111111111"/>
Там, где кракозябры - это две буквы "И".
  Я его загружаю с помощью QTextStream с отключенным автоопределением кодировки, затем перекодирую в Unicode с использованием кодека QTextCodec::codecForName("UTF-8"). Получаю
Код:
<?xml version="1.0" encoding="UTF-8"?>
<ED Acc="11111�?�?1111111111111"/>
Замечу, что остальные русские буквы перекодируются нормально. Более того, если исходный файл в KOI8-R, ISO-8859-5, windows-1251, то все буквы перекодируются нормально.
  Если читать этот же файл с использованием QXmlSimpleReader, то значение атрибута передается правильно:
Код:
Acc="11111ИИ1111111111111"
(попутно замечу, что этот класс совместно с QXmlStreamWriter - очень неплохое решение для работы с XML, так что вопрос, с чего начался топик, решен в их пользу, библиотека почти готова, затормозил на демонстрационном примере.)
  Кодеки ForTr, ForLocale, ForCStrings установлены в "windows-1251".
  Такое ощущение, что я что-то не понимаю (и не только я - топики с кодировками тут обширные, я их полистал).
 
  На всякий случай, некоторые куски кода:
Код:
    QTextCodec *cp1251 = QTextCodec::codecForName("windows-1251");
    QTextCodec::setCodecForTr(cp1251);
    QTextCodec::setCodecForLocale(cp1251);
    QTextCodec::setCodecForCStrings(cp1251);

            QTextStream stream(&file);
            stream.setAutoDetectUnicode(false);
            QString text=stream.readAll();
            // это исходный текст
            textEdit->append(text);

        QByteArray xml;
        xml.append(text);
        QTextCodec *codec = QTextCodec::codecForName("UTF-8");
        // это перекодированный
        textEdit->append(codec->toUnicode(xml)); 




Название: Re: Чтение XML, запись XML
Отправлено: SABROG от Февраль 26, 2010, 03:05
Код
C++ (Qt)
QTextCodec::setCodecForLocale(cp1251);
...
QTextStream stream(&file);
           stream.setAutoDetectUnicode(false);
...
QString text=stream.readAll();
...
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
// это перекодированный
textEdit->append(codec->toUnicode(xml));

Что вижу я из этого кода. Ставим локаль в cp1251, открываем поток, который берет текущий кодек локали (cp1251) и отключаем определение кодировок UTF-16 и UTF-32 через BOM (который не добавляют большинство текстовых редакторов). В строчке с stream.readAll() в text текст попадает уже в юникоде. cp1251->Unicode 4.0
Затем мы берем строку text (которая у нас уже в Unicode 4.0) и добавляем в QByteArray. Выбираем кодек UTF-8 и ...: выполняем преобразование Unicode 4.0 в Unicode 4.0, обманывая кодек, сказав ему, что у нас там UTF-8.
Но UTF8/16/32 и т.д != Unicode. UTF - представление кодировки, где тот же английский текст записывается как один байт на символ, в то время как в Unicode отводится 2 байта на любой символ.

В какой бы ты кодировке не грузил файл через QTextStream содержимое будет всегда конвертироваться как cp1251->Unicode, так как ты сам выбрал кодировку для локали. И не важно в какой кодировке сам файл, QTextStream не умеет определять кодировки налету. Поэтому либо вызывай QTextStream::setCodec(), которому подставляй кодировку в которой у тебя файл, либо в конструктор подставляй QString, предварительно считав содержимое из файла (QFile) и преобразовав кодировку вручную.


Название: Re: Чтение XML, запись XML
Отправлено: sergek от Февраль 26, 2010, 20:15
Цитировать
В строчке с stream.readAll() в text текст попадает уже в юникоде. cp1251->Unicode 4.0
Верно.
Цитировать
Затем мы берем строку text (которая у нас уже в Unicode 4.0) и добавляем в QByteArray. Выбираем кодек UTF-8 и ...: выполняем преобразование Unicode 4.0 в Unicode 4.0, обманывая кодек, сказав ему, что у нас там UTF-8
А это не так. Вы пропустили пару строк из моего поста:
Код:
QByteArray xml;
xml.append(text);
При выполнении этой операции неявно используется метод QString::toAscii (), который, в свою очередь использует codecForCStrings(). У меня все кодеки одинаковые, поэтому должно выполняться обратное преобразование Unicode->cp1251, и в xml текст должен быть в честном UTF-8.
Для всех других русских букв, кроме "И" это выполняется, попробуйте.
Цитировать
QTextStream не умеет определять кодировки налету
Если есть BOM, то умеет.

В общем, пока причина непонятна. Но то, что мне нужно, я получил - просто выкинул QTextStream за ненадобностью:
Цитировать
QFile file(fileName);
file.open(QFile::ReadOnly | QFile::Text);
QByteArray xml=file.readAll();
Таким образом, взаимная перекодировка устранена, глюков нет.
Спасибо за подсказки.


Название: Re: Чтение XML, запись XML
Отправлено: SABROG от Февраль 27, 2010, 00:52
В общем, пока причина непонятна.

Возможно происходит что-то о чем сказано здесь:

Цитировать
Warning: Some codecs do not preserve the characters in the ASCII range (0x00 to 0x7F). For example, the Japanese Shift-JIS encoding maps the backslash character (0x5A) to the Yen character. To avoid undesirable side-effects, we recommend avoiding such codecs with setCodecsForCString().

Для проверки верни QTextStream и просто вызови QByteArray::append(). А потом сохрани его содержимое в файл.

Я никогда не использовал глобальную установку кодеков вручную и делать этого никогда не буду и никому не советую.


Название: Re: Чтение XML, запись XML
Отправлено: sergek от Март 01, 2010, 10:34
Цитировать
У меня все кодеки одинаковые, поэтому должно выполняться обратное преобразование Unicode->cp1251, и в xml текст должен быть в честном UTF-8
Причина, более-менее, понятна - в неэквивалентности прямых и обратных преобразований. Наверное, можно выстроить эти цепочки, но я сделал по-другому. XML-файл загружаю в QByteArray, сохраняя кодировку. Затем определяю кодировку этого файла, используя QXmlSimpleReader преобразую исходный текст честным кодеком (соответствующим кодировке) в unicode - для его просмотра и редактирования. Когда нужен исходный текст, выполняю обратное преобразование из unicode в первоначальную кодировку. В общем, решение очевидное.

Цитировать
Я никогда не использовал глобальную установку кодеков вручную и делать этого никогда не буду и никому не советую
Прокомментируйте, пожалуйста. Например, чем плох вариант:
Код:
    QTextCodec * codec = QTextCodec::codecForLocale();

    QTextCodec::setCodecForTr(codec);
    QTextCodec::setCodecForCStrings(codec);


Название: Re: Чтение XML, запись XML
Отправлено: SABROG от Март 01, 2010, 13:47
Слово "вручную" означало в данном контексте "жестко заданную". В то время как codecForLocale() автоматическая, это я использую в своих приложениях и то для нормального вывода отладочной информации в консоль.

А вот это явно лишнее. Кодировка исходников может не совпадать с кодировкой локали.
Код:
QTextCodec::setCodecForTr(codec);
QTextCodec::setCodecForCStrings(codec);


Название: Re: Чтение XML, запись XML
Отправлено: sergek от Март 01, 2010, 14:10
Еще вопрос. Для определения кодировки исходного xml-файла в обработчике QXmlSimplReader я делаю
Код:
class CUfebsHandler : public QXmlDefaultHandler
{...

bool CUfebsHandler::processingInstruction ( const QString & target, const QString & data ){
    encoding="UTF-8";
    if(target=="xml"){
        int pos=data.indexOf("encoding");
        if(pos>=0){
            encoding=data.mid(pos).section('\'',1,1).trimmed();
        }
    }
    return true;
}
Остается ощущение, что это велосипед. Нет ли стандартных средств?


Название: Re: Чтение XML, запись XML
Отправлено: SABROG от Март 01, 2010, 14:28
Остается ощущение, что это велосипед. Нет ли стандартных средств?

Есть. Все классы использующие QXmlInputSource автоматически определяют кодировку, если они не могут её определить, то используется UTF-8 или UTF-16.


Название: Re: Чтение XML, запись XML
Отправлено: sergek от Март 02, 2010, 10:24
Цитировать
Все классы использующие QXmlInputSource автоматически определяют кодировку
Я в этом и не сомневался :) А выдают ли они ее наружу? Как в обработчиках получить значение encoding из декларации?


Название: Re: Чтение XML, запись XML
Отправлено: SABROG от Март 02, 2010, 12:38
Не выдают. Кодек хранится в указателе d_ptr (QXmlSimpleReaderPrivate)  private секции класса QXmlSimpleReader. Достучаться можно унаследовав QXmlSimpleReader через using.


Название: Re: Чтение XML, запись XML
Отправлено: sergek от Март 12, 2010, 22:59
Коллеги! Проект (библиотека для работы с xml-документами определенного формата), в связи с которым я и начал этот топик, почти завершен. Если есть желание, можно ознакомиться с ним по ссылке:
http://get.freesoft.ru/?id=106788 (http://get.freesoft.ru/?id=106788)
Архив включает скомпилированный демонстрационный пример для Windows.

Если влом тянуть весь проект (4.6 Мб), можно ознакомиться с документацией на него (руководство программиста):
http://get.freesoft.ru/?id=106789 (http://get.freesoft.ru/?id=106789)

Надеюсь, что вы найдете там кое-что полезное, тем более, что я попытался выработать некоторый общий подход к решению подобных задач. Если кто-то не сочтет за труд это прочитать и высказать свои суждения, буду весьма признателен.

Со своей стороны прошу all поделиться функцией формирования суммы прописью, которую можно было бы включить в библиотеку. Собственно, у меня есть такая, но я не знаю автора, и неудобно включать ее во фриварный софт.


Название: Re: Чтение XML, запись XML
Отправлено: sergek от Март 21, 2010, 15:36
Выпущена новая версия библиотеки (ссылки выше исправлены). Если кому интересно, обновления смотрите на сайте, указанном в документации (после модерации программы).
Тему завершаю, всем спасибо.


Название: Re: [решено] Чтение XML, запись XML
Отправлено: voronElf от Март 22, 2010, 09:09
Насчет формирования суммы прописью, отдаю свою как есть. Только писал давно, зеленый был, ногами сильно не бить )))

Код:
QString CModDocs::priceToText(double price)
{
    if(price > 20000000) return tr("Не могу посчитать такие большие деньги ...");
    unsigned long int rub = (int)price;
    int kop = int(price*100) - rub*100;
    if(rub == 0) return tr("Ноль рублей %1 коп.").arg(kop);

    QString ret("");
    QStringList sotni;
    sotni.append(tr(""));
    sotni.append(tr("сто "));
    sotni.append(tr("двести "));
    sotni.append(tr("триста "));
    sotni.append(tr("четыреста "));
    sotni.append(tr("пятьсот "));
    sotni.append(tr("шестьсот "));
    sotni.append(tr("семьсот "));
    sotni.append(tr("восемьсот "));
    sotni.append(tr("девятьсот "));
    QStringList desatki;
    desatki.append(tr(""));
    desatki.append(tr("десять "));
    desatki.append(tr("двадцать "));
    desatki.append(tr("тридцать "));
    desatki.append(tr("сорок "));
    desatki.append(tr("пятьдесят "));
    desatki.append(tr("шестьдесят "));
    desatki.append(tr("семьдесят "));
    desatki.append(tr("восемьдесят "));
    desatki.append(tr("девяносто "));
    QStringList edinic;
    edinic.append(tr(""));
    edinic.append(tr("один "));
    edinic.append(tr("два "));
    edinic.append(tr("три "));
    edinic.append(tr("четыре "));
    edinic.append(tr("пять "));
    edinic.append(tr("шесть "));
    edinic.append(tr("семь "));
    edinic.append(tr("восемь "));
    edinic.append(tr("девять "));
    QStringList edinic_10;
    edinic_10.append(tr("десять "));
    edinic_10.append(tr("одиннадцать "));
    edinic_10.append(tr("двенадцать "));
    edinic_10.append(tr("тринадцать "));
    edinic_10.append(tr("четырнадцать "));
    edinic_10.append(tr("пятнадцать "));
    edinic_10.append(tr("шестнадцать "));
    edinic_10.append(tr("семнадцать "));
    edinic_10.append(tr("восемнадцать "));
    edinic_10.append(tr("девятнадцать "));

    int r[9], i; // в r индексы - номера разрядов
    for(i=0; i<9; i++) if(rub>0)
            {
                r[i] = rub%10; rub = rub/10;
            }else r[i]=0;

    if(r[8]>0 || r[7]>0 || r[6]>0){
        ret += sotni[r[8]];
        if( r[7]==1 ) ret += edinic_10[r[6]] + tr("милионов ");
        else{
            ret += desatki[r[7]] + edinic[r[6]];
            if(r[6] == 1) ret += tr("милион ");
            else if(r[6]==2 || r[6]==3 || r[6]==4) ret += tr("милиона ");
            else ret += tr("милионов ");
        }
    }
    if(r[5]>0 || r[4]>0 || r[3]>0){
        ret += sotni[r[5]];
        if(r[4] == 1) ret += edinic_10[r[3]] + tr("тысяч ");
        else{
            ret += desatki[r[4]];
            if(r[3] == 1) ret += tr("одна тысяча ");
            else if( r[3]==2 ) ret += tr("две тысячи ");
            else{
                ret += edinic[r[3]];
                if( r[3]==3 || r[3]==4) ret += tr("тысячи "); else ret += tr("тысяч ");
            }
        }
    }
    ret += sotni[r[2]];
    if( r[1]==1 ) ret += edinic_10[r[0]] + tr("рублей ");
    else{
        ret += desatki[r[1]] + edinic[r[0]];
        if(r[0] == 1) ret += tr("рубль ");
        else if(r[0]==2 || r[0]==3 || r[0]==4) ret += tr("рубля ");
        else ret += tr("рублей ");
    }

    ret += tr(" %1 коп.").arg(kop);
    ret[0] = ret[0].toUpper();
    return ret;
}