Boost.Locale под Windows
От: sunheretic13  
Дата: 22.08.12 06:45
Оценка:
Имеется программа

#include <conio.h>
#include <iostream>
#include <boost/locale.hpp>

using namespace boost::locale;
using namespace std;


int wmain()
{
    generator gen;

    locale loc=gen("ru_RU.UTF-8");

    wcout.imbue(loc);

    wcout<<L"Русский текст"<<endl;

    _getch();

    return 0;
}





Программа должна корректно выводить текст в консоли Windows в кодировке UTF-8.
Понятное дело что просто так она это не выводит.
Ставлю для cmd.exe шрифт Lucinda Console.
Ставлю кодировку — chcp 65001 (кодировка UTF-8).

Запускаю в полученной среде программу.
На экране либо пустота либо квадраты (Windows XP/7).
Делаю операцию


Test.exe >1.txt

type 1.txt



На экране:


Русский текст.




Что за нафиг? Как это лечить? Я хочу чтобы в самой программе вывод был нормальный.
Re: Boost.Locale под Windows
От: nen777w  
Дата: 22.08.12 07:07
Оценка:
L"Русский текст" <- Разве результатом этого не будет utf16?

В свое время когда надо было utf8 <-> utf16, пользовался вот таким откуда то спертым кодом

  Скрытый текст
//.................................................................................................
    template<class InIt, class OutIt>
    inline void utf8_encode(InIt in, const InIt end, OutIt out)
    {
        while( !(in == end) ) {
            unsigned long wc = static_cast<wchar_t>(*in); ++in;
        over:
            if( wc < 0x80 ) {
                *out = static_cast<char>(wc); ++out;
                continue;
            }

            if( sizeof(wchar_t) == 2 && 
                wc >= 0xD800 && wc < 0xE000 ) 
            {//handle surrogates for UTF-16
                if( wc >= 0xDC00 ) { wc = '?'; goto over; }
                if( in == end ) return;
                unsigned long lo = static_cast<wchar_t>(*in); ++in;
                if( lo >= 0xDC00 && wc < 0xE000 ) {
                    wc  = 0x10000 + ((wc & 0x3FF) << 10 | lo & 0x3FF);
                } else { 
                    *out = '?'; ++out; wc = lo;
                    goto over;
                }
            }

            char c; int shift; 
            if( wc < 0x800 )     { shift = 6;  c = ('\xFF' << 6); } else
                if( wc < 0x10000 )   { shift = 12; c = ('\xFF' << 5); } else
                    if( wc < 0x200000 )  { shift = 18; c = ('\xFF' << 4); } else
                        if( wc < 0x4000000 ) { shift = 24; c = ('\xFF' << 3); } else 
                        { shift = 30; c = ('\xFF' << 2); }
                        do {
                            c |= (wc >> shift) & 0x3f;
                            *out = c; ++out;
                            c = char(0x80); shift -= 6;
                        } while( shift >= 0 );
        }
    }
    //.................................................................................................
    template<class InIt, class OutIt>
    inline void utf8_decode(InIt in, const InIt end, OutIt out)
    {
        int cnt;
        for(; !(in == end); ++in) {
            unsigned long wc = static_cast<unsigned char>(*in);
        over:
            if( wc & 0x80 ) {
                if( 0xC0 == (0xE0 & wc) ) { cnt = 1; wc &= ~0xE0; } else
                    if( 0xE0 == (0xF0 & wc) ) { cnt = 2; wc &= ~0xF0; } else
                        if( 0xF0 == (0xF8 & wc) ) { cnt = 3; wc &= ~0xF8; } else
                            if( 0xF8 == (0xFC & wc) ) { cnt = 4; wc &= ~0xFC; } else
                                if( 0xFC == (0xFE & wc) ) { cnt = 5; wc &= ~0xFE; } else
                                { *out = wchar_t('?'); ++out; continue; };//invalid start code
                if( 0 == wc ) wc = ~0UL;//codepoint encoded with overlong sequence
                do {
                    if( ++in == end ) return;
                    unsigned char c = static_cast<unsigned char>(*in);
                    if( 0x80 != (0xC0 & c) )
                    { *out = static_cast<wchar_t>(wc); ++out; wc = c; goto over; }
                    wc <<= 6; wc |= c & ~0xC0;
                } while( --cnt );
                if( 0x80000000 & wc ) wc = '?';//codepoint exceeds unicode range
                if( sizeof(wchar_t) == 2 && wc > 0xFFFF )
                {//handle surrogates for UTF-16
                    wc -= 0x10000;
                    *out = static_cast<wchar_t>(0xD800 | ((wc >> 10) & 0x3FF)); ++out;
                    *out = static_cast<wchar_t>(0xDC00 | (wc & 0x3FF)); ++out;
                    continue;
                }
            }
            *out = static_cast<wchar_t>(wc); ++out;
        }
    }
    //.................................................................................................
    inline std::string    utf16_to_utf8( const std::wstring& utf16 )
    {
        std::string result;
        utf8_encode( utf16.begin(), utf16.end(), std::back_inserter(result) );
        return result;
    }
    //.................................................................................................
    inline std::wstring utf8_to_utf16( const std::string& utf8 )
    {
        std::wstring result;
        utf8_decode( utf8.begin(),utf8.end(), std::back_inserter(result) );
        return result;
    }
Re[2]: Boost.Locale под Windows
От: sunheretic13  
Дата: 22.08.12 07:25
Оценка:
Здравствуйте, nen777w, Вы писали:

L"Русский текст" — это действительно будет utf-16
В моём примере оно преобразуется внутри системных библиотек в utf-8 И плюётся на экран. type же отрабатывает правильно с выводом программы.

В том то и дело что хочу написать реально кросплатформенную программу, используя некое внутреннее представление строк и символов и кодировку utf-8 как выводную посредством boost.
Ибо на юниксе однобайтовые кодировки померли — остался utf-8.
А винду можно заставить работать в utf-8.

Задача стоит не в том чтобы написать ещё какой-то конвертер одной кодировки в другую (это не требуется — всё уже проделано до меня). Задача — в правильном отображении самого ВЫВОДА ПРОГРАММЫ, а не результатов складированных в файл.
Re: Boost.Locale под Windows
От: 5er Россия  
Дата: 22.08.12 11:21
Оценка: +1
Здравствуйте, sunheretic13, Вы писали:

S>Что за нафиг? Как это лечить? Я хочу чтобы в самой программе вывод был нормальный.


std::locale::global(std::locale("Russian"));
wcout<<L"Русский текст"<<endl;
Re[2]: Boost.Locale под Windows
От: sunheretic13  
Дата: 22.08.12 11:27
Оценка:
Здравствуйте, 5er, Вы писали:

Задача не в том чтобы вывести русский текст в консоль. Это и ребёнок сделает.
Задача стоит в том чтобы вывести utf-8 в консоль и она корректно отображалась(это при том что преобразование работает правильно — проверял, локаль консоли тоже правильно настроена).
Re: Boost.Locale под Windows
От: Кодт Россия  
Дата: 22.08.12 19:00
Оценка: 1 (1)
Здравствуйте, sunheretic13, Вы писали:

S>Что за нафиг? Как это лечить? Я хочу чтобы в самой программе вывод был нормальный.


(Сижу не в винде, поэтому только по старой памяти могу сказать)

Во-первых, wcout отличается коварным поведением: он различает, подключен stdout непосредственно к консоли (utf-16) или к файлу/пайпу (поток байтов).
Соответственно, и конвертирует он туда или оттуда.
Не помню, обладает ли wprintf тем же свойством, или это специфика именно wcout (склоняюсь к последнему). За подробностями — смотри исходники стандартной библиотеки, в частности, ищи использование функции isatty.

Поэтому test.exe и test.exe>1.txt выполняют разную работу.

Во-вторых, в винде локали называются атипично: не ru_RU, а rus_RUS, он же Russian... Из-за неправильного названия setlocale может банально не сработать, и ты окажешься с дефолтной локалью вместо ожидаемой.

В-третьих, вайдовый литерал L"Русский текст" в сишном исходнике — записан в какой-то байтовой кодировке (как, собственно, и весь исходник).
Текстовый редактор считает, что это кодировка X (на ней "Русский текст" выглядит правильно); компилятор — что это кодировка Y. И именно из Y переводит в utf-16, чтобы поместить в секцию констант.
Чтобы кодировки редактора и компилятора совпадали, нужно или оставаться с дефолтными настройками (тогда они обе будут cp1251, скорее всего), или приложить какие-то дополнительные усилия (например, если кодировка редактора — utf-8...)
За подробностями — читать хелп по VisualStudio.

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

1. Кодировка редактора — utf-8, компилятора — cp1251. L"Русский текст" с точки зрения компилятора выглядит как L"Р СѓСЃСЃРєРёР№ текст"
2. Локаль выбрать не удалось, она осталась системной
3. При выводе прямо в консоль получили мусор как есть
4. При выводе в файл мусор из utf-16 сконвертировали в cp1251, тем самым, восстановив старый utf-8 "Русский текст"
5. При распечатке файла — консоль переводит байты из кодировки 65001 в utf-16, и мы прекрасно видим "Русский текст"



Это мы разобрались, кого виновать. Теперь — что делать.

1) Отказаться от L-литералов. Ибо они сразу три беды вместе увязывают: кодировку редактора, компилятора и wcout'а.
Если очень хочется писать русский текст в литералах, а не в файлах переводов (когда программа маленькая и одноязычная, то морочиться с переводами не хочется) — то надо прибить гвоздями кодировку редактора и эту же кодировку упомятуть в setlocale в тексте программы.

2) Отказаться от wcout.

3) Про буст ничего не скажу, — только мне кажется, что он здесь излишний, достаточно было средств <locale>. Если только он не решает проблему альтернативных названий кодировок под виндами.
Перекуём баги на фичи!
Re[3]: Boost.Locale под Windows
От: Кодт Россия  
Дата: 22.08.12 19:21
Оценка:
Здравствуйте, sunheretic13, Вы писали:

S>Задача не в том чтобы вывести русский текст в консоль. Это и ребёнок сделает.

S>Задача стоит в том чтобы вывести utf-8 в консоль и она корректно отображалась(это при том что преобразование работает правильно — проверял, локаль консоли тоже правильно настроена).

Кстати, в качестве дополнительной проверки можно попробовать вывести хардкоднутые строки:
— L"\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \u0442\u0435\u043a\u0441\u0442"
— "\xd0\xa0\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 \xd1\x82\xd0\xb5\xd0\xba\xd1\x81\xd1\x82"
Тем самым мы удостоверимся, что, по крайней мере, связка редактор-компилятор не вмешалась.
И заодно, что оба преобразования — в utf-16 и в utf-8 — сработали корректно как для вывода в консоль, так и для вывода в файл.
Перекуём баги на фичи!
Re[2]: Boost.Locale под Windows
От: Кодт Россия  
Дата: 22.08.12 19:26
Оценка:
К>1) Отказаться от L-литералов. Ибо они сразу три беды вместе увязывают: кодировку редактора, компилятора и wcout'а.
К>Если очень хочется писать русский текст в литералах, а не в файлах переводов (когда программа маленькая и одноязычная, то морочиться с переводами не хочется) — то надо прибить гвоздями кодировку редактора и эту же кодировку упомятуть в setlocale в тексте программы.

Я имел в виду — писать текст в байтовых литералах, т.е. "Русский текст".
Перекуём баги на фичи!
Re[3]: Boost.Locale под Windows
От: sunheretic13  
Дата: 23.08.12 06:46
Оценка:
Здравствуйте, Кодт, Вы писали:

ЭЭх.
Задача как раз и стояла:
1. Разобраться с Boost.Locale .
2. Использовать кроссплатформенное именование локали вроде ru_RU.UTF-8 вместо того чтобы на Windows изголяться локалями в виде "Russian".

Вообщем попробовал следующее.

1. Попробовал вбить этот текст — L"\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \u0442\u0435\u043a\u0441\u0442\xd0\xa0\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 \xd1\x82\xd0\xb5\xd0\xba\xd1\x81\xd1\x82"
Не помогло
2. Прочитал инструкцию — http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/running_examples_under_windows.html Люди советуют файлы исходников сохранять в файлы utf-8 с BOM-заголовком.
Сделал. Проверял и с
cout<<"Русский текст"

и
wcout<<"Русский текст"

и

wcout<<L"Русский текст"

Ничего не работает.


Я сколняюсь к мысли что видимо команда Boost не до конца оттестировала эту часть библитеки, либо делает какие-то особые настройки в Windows не описанные в руководстве.

Видимо те локали что генерируются boost-библиотекой какие-то "неправильные" и не подходят для вывода в консоль. Наверно придётся писать в багрепорт Boost, ибо я совершенно не понимаю как заставить этот пример работать на Windows.
Re[4]: Boost.Locale под Windows
От: Кодт Россия  
Дата: 23.08.12 07:52
Оценка:
Здравствуйте, sunheretic13, Вы писали:

S>Задача как раз и стояла:

S>1. Разобраться с Boost.Locale .
S>2. Использовать кроссплатформенное именование локали вроде ru_RU.UTF-8 вместо того чтобы на Windows изголяться локалями в виде "Russian".

А обязательно нужно русскую локаль целиком подцепить? Может, достаточно ограничиться только выбором кодировки — ".utf-8"?


S>Ничего не работает.


А если вместо wcout.imbue(loc) делать std::locale::global(gen) ?
А если создавать локаль без буста, — std::locale loc(".utf-8") ?
Перекуём баги на фичи!
Re[5]: Boost.Locale под Windows
От: sunheretic13  
Дата: 23.08.12 09:29
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А если создавать локаль без буста, — std::locale loc(".utf-8") ?


А если вместо wcout.imbue(loc) делать std::locale::global(gen)

Сделал сразу же как только начал изучать вопрос — не работает.

А если создавать локаль без буста, — std::locale loc(".utf-8") ?

Сделал
locale loc=gen(".UTF-8");


Вывод отсутствует.
Re[6]: Boost.Locale под Windows
От: sunheretic13  
Дата: 23.08.12 11:16
Оценка:
Только что попробовал следующие 2 строчки.

generator gen;
locale loc=gen("bred");


Эти две строчки работают если их выполнить.

Наверно стоит закрыть тему.
Вердикт — Boost.Locale очень сырая часть библиотеки boost, не пригодная для использования в программах.
Re[7]: Boost.Locale под Windows
От: Кодт Россия  
Дата: 23.08.12 18:11
Оценка:
Здравствуйте, sunheretic13, Вы писали:

S>Наверно стоит закрыть тему.

S>Вердикт — Boost.Locale очень сырая часть библиотеки boost, не пригодная для использования в программах.

Видимо, да.
Проще сделать условную компиляцию: если под винды, то locale("rus_RUS.utf-8"), иначе ("ru_RU.utf-8").
Или просто locale(".utf-8"), что должно сработать повсеместно.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.