тысяча второй раз про Юникод
От: pepsicoca  
Дата: 13.10.09 13:28
Оценка:
Добрый день.

Спрошу еще раз, потому что в старой ветке никто не читает.

Есть MSVC2003 и есть код.


// example.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
//    std::wcout.imbue(std::locale("rus_rus.866"));
 


    std::wcout<<L"hello"<<std::endl;
    std::wcout<<L"привет"<<std::endl;
    return 0;
}


Если закомментировать строчку std::wcout.imbue(std::locale("rus_rus.866"));, то русский "привет" не печатается.

Вот это мне как раз и непонятно. На то и Юникод, чтобы забыть о переключении таблиц символов. Если есть Юникодная строка, зачем мне указывать локаль для потока? Код Юникодного символа ОДНОЗНАЧНО определяет его начертание и принадлежность к тому или иному алфавиту. Зачем еще указывать локаль? Если текущий код это китайский иероглиф — пусть система напечатает китайский иероглиф. Если текущий код это греческая буква "кси" — пусть система напечатает греческую букву "кси". При чем тут локаль — совершенно непонятно. По моим понятиям, когда компилятор встречает в исходнике строку

L"привет"


он должен в объектнике создать Юникодную строку с русскими буквами. А дальше работать с Юникодом без указания на локаль.

Спасибо.
Re: тысяча второй раз про Юникод
От: Сергей Мухин Россия  
Дата: 13.10.09 13:38
Оценка:
Здравствуйте, pepsicoca, Вы писали:

P>
P>L"привет"
P>


P>он должен в объектнике создать Юникодную строку с русскими буквами. А дальше работать с Юникодом без указания на локаль.


Откуда компилятор знает, что они русские?

#pragma setlocale( "[locale-string]" )
---
С уважением,
Сергей Мухин
Re[2]: тысяча второй раз про Юникод
От: pepsicoca  
Дата: 13.10.09 13:47
Оценка:
Здравствуйте, Сергей Мухин, Вы писали:

СМ>Здравствуйте, pepsicoca, Вы писали:


P>>он должен в объектнике создать Юникодную строку с русскими буквами. А дальше работать с Юникодом без указания на локаль.


СМ>Откуда компилятор знает, что они русские?


СМ>#pragma setlocale( "[locale-string]" )


Очевидно, от редактора? В любом случае imbue это механизм времени исполнения, а к этому времени Юникодная строка

P>>
P>>L"привет"
P>>


Должна уже лежать в экзешнике.

Кстати, попробовал пример по указанной ссылке.

#pragma setlocale( "rus_rus.866" )
#pragma setlocale( "russian" )

Оба варианта не работают.
Re[2]: тысяча второй раз про Юникод
От: igna Россия  
Дата: 13.10.09 13:48
Оценка:
Здравствуйте, Сергей Мухин, Вы писали:

СМ>Откуда компилятор знает, что они русские?


Ну когда спрашивающий раскомментирует (в смысле раскомментировывает ) std::wcout.imbue(std::locale("rus_rus.866")), то "привет" по-видимому печатается, так что проблема не в том, что компилятор не знает, руские там буквы или нет.
Re[3]: тысяча второй раз про Юникод
От: pepsicoca  
Дата: 13.10.09 14:05
Оценка:
СМ>>Откуда компилятор знает, что они русские?

СМ>>#pragma setlocale( "[locale-string]" )


P>Очевидно, от редактора? В любом случае imbue это механизм времени исполнения, а к этому времени Юникодная строка


Ну да, от ASCIIшного редактора компилятор это знать не может. То есть если по-честному, то программы с использованием Юникода должны набираться в Юникодном редакторе, а транслятор должен уметь транслировать Юникодные исходники.

Вопрос:

1. Умеет ли редактор и транслятор MSVC работать в Юникодными исходниками? Если да, то где этот режим включается?

Спасибо
Re[3]: тысяча второй раз про Юникод
От: Kh_Oleg  
Дата: 13.10.09 14:12
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, Сергей Мухин, Вы писали:


СМ>>Откуда компилятор знает, что они русские?


I>Ну когда спрашивающий раскомментирует (в смысле раскомментировывает ) std::wcout.imbue(std::locale("rus_rus.866")), то "привет" по-видимому печатается, так что проблема не в том, что компилятор не знает, русcкие там буквы или нет.


Именно, что не знает. Смотрите: первые 128 символов в ASCII и Unicode совпадают — поэтому латинские буквы печатаютcя.
Слово "привет" в исходном файле на С++, представляет собой некоторую последовательность чисел из диапазона выше 128. Так устроен формат ASCII, в котором сохраняется С++ исходник. Просто последовательность чисел. При компиляции компилятор читает этот файл (не имея при этом связи с редактором) и в зависимости от того, к какой locale мы хотим отнести данную последовательность кодов, он определяет диапазон кодов unicode, в которые следует произвести конвертацию. Если указана русская locale, эти числа будут конвертированы в Unicode диапазон 0400h-04FFh, если мы, скажем, укажем японскую locale, то эти же числа из исходного текста будут конвертированы в другой диапазон кодов Unicode — 3040h-30FFh. И, соответственно, отображаться на экране такая строка будет по-разному, в зависимости от locale.

Вот как-то так.
Re[4]: тысяча второй раз про Юникод
От: igna Россия  
Дата: 13.10.09 14:21
Оценка:
Здравствуйте, Kh_Oleg, Вы писали:

K_O>Если указана русская locale, эти числа будут конвертированы в Unicode диапазон 0400h-04FFh, если мы, скажем, укажем японскую locale, то эти же числа из исходного текста будут конвертированы в другой диапазон кодов Unicode — 3040h-30FFh.


Ты не хочешь ли сказать, что выполняемое на этапе компиляции преобразование зависит от того, закоментирована ли std::wcout.imbue(std::locale("rus_rus.866"))?
Re: тысяча второй раз про Юникод
От: pepsicoca  
Дата: 13.10.09 14:24
Оценка:
Здравствуйте, pepsicoca, Вы писали:

Модифицировал пример и самостоятельно сформировал Юникодную строку L"привет" в виде массива.

// example.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

//#pragma setlocale( "rus_rus.866" )
#pragma setlocale( "russian" )

wchar_t array[]={0x043f,0x0440,0x0438,0x0432,0x0435,0x0442,0};

int _tmain(int argc, _TCHAR* argv[])
{
//    std::wcout.imbue(std::locale("rus_rus.866"));
 


    std::wcout<<L"hello"<<std::endl;
//    std::wcout<<L"привет"<<std::endl;
    std::wcout<<array<<std::endl;
    return 0;
}


где

wchar_t array[]={0x043f,0x0440,0x0438,0x0432,0x0435,0x0442,0};


Это юникодный русский L"привет", написанный мной в кодах и с нулем на конце.

Этот пример НЕ печатает русский "привет".

Очевидно, что std::wcout действительно работает все-таки не с Юникодом, а с локалью+код смещения из второй половины таблицы. Но при этом появляются новые вопросы:

Вопросы:

1. Для китайского языка смещение гораздо больше, чем 128 символов из второй половины ASCII-таблицы. Как же в ASCIIшном исходнике задать китайскую строку, хотя бы и с помощью китайской локали?

2. А честный Юникодный исходник бывает? И как тогда работает std::wcout? Или у честного Юникодного исходника и реализация библиотеки std::wcout другая и эта другая реализация библиотеки тоже честно работает с Юникодом, а не с локалью?

Спасибо.
Re[5]: тысяча второй раз про Юникод
От: Kh_Oleg  
Дата: 13.10.09 14:28
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, Kh_Oleg, Вы писали:


K_O>>Если указана русская locale, эти числа будут конвертированы в Unicode диапазон 0400h-04FFh, если мы, скажем, укажем японскую locale, то эти же числа из исходного текста будут конвертированы в другой диапазон кодов Unicode — 3040h-30FFh.


I>Ты не хочешь ли сказать, что выполняемое на этапе компиляции преобразование зависит от того, закоментирована ли std::wcout.imbue(std::locale("rus_rus.866"))?


Мне кажется, что преобразование происходит не на этапе компиляции.
Re[6]: тысяча второй раз про Юникод
От: igna Россия  
Дата: 13.10.09 14:36
Оценка:
Здравствуйте, Kh_Oleg, Вы писали:

K_O>Мне кажется, что преобразование происходит не на этапе компиляции.


Да? А в исполняемом файле его L"привет" как хранится? Прямо так как написано, безо всякого преобразования в коды?
Re[6]: тысяча второй раз про Юникод
От: pepsicoca  
Дата: 13.10.09 14:38
Оценка:
Здравствуйте, Kh_Oleg, Вы писали:

K_O>Здравствуйте, igna, Вы писали:


I>>Здравствуйте, Kh_Oleg, Вы писали:


K_O>>>Если указана русская locale, эти числа будут конвертированы в Unicode диапазон 0400h-04FFh, если мы, скажем, укажем японскую locale, то эти же числа из исходного текста будут конвертированы в другой диапазон кодов Unicode — 3040h-30FFh.


I>>Ты не хочешь ли сказать, что выполняемое на этапе компиляции преобразование зависит от того, закоментирована ли std::wcout.imbue(std::locale("rus_rus.866"))?


K_O>Мне кажется, что преобразование происходит не на этапе компиляции.


Скорее всего именно так. То есть wcout выводит коды, а imbue указывает, какую таблицу нужно брать.

Но тогда встают вопросы:

1. Где хранятся ВСЕ таблицы? В CRT? В экзешнике?

2. Очевидно, что если imbue это механизм времени исполнения, то хранится должны ВСЕ таблицы, а imbue только выбирает нужную. Не многовато-ли хранить ВСЕ таблицы в экзешнике? Для винды это 65тыщ кодов, все-таки обозримо. А для Юникса-то 4 млрд кодов(!).

3. Как задать китайские строки, кодов в которых гораздо больше, чем 128 кодов из второй половины таблицы ASCII?
Re[7]: тысяча второй раз про Юникод
От: Kh_Oleg  
Дата: 13.10.09 15:01
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, Kh_Oleg, Вы писали:


K_O>>Мне кажется, что преобразование происходит не на этапе компиляции.


I>Да? А в исполняемом файле его L"привет" как хранится? Прямо так как написано, безо всякого преобразования в коды?

Как оказалось, в исполняемом файле хранится правильная Unicode строка. Очевидно, компилятор использует текущий locale, используемый в OC.
А вот почему stream не может отображать такие строки без явного задания locale — это уже отдельный вопрос, какие-то их внутренние заморочки.

Кстати, а какой locale установлен в ОС топикстартера? Если английский/американский — тогда можно объяснить такое поведение, если русский — тогда не знаю.
Re[8]: тысяча второй раз про Юникод
От: pepsicoca  
Дата: 13.10.09 15:26
Оценка:
Здравствуйте, Kh_Oleg, Вы писали:

K_O>Здравствуйте, igna, Вы писали:


I>>Здравствуйте, Kh_Oleg, Вы писали:


K_O>>>Мне кажется, что преобразование происходит не на этапе компиляции.


I>>Да? А в исполняемом файле его L"привет" как хранится? Прямо так как написано, безо всякого преобразования в коды?

K_O>Как оказалось, в исполняемом файле хранится правильная Unicode строка. Очевидно, компилятор использует текущий locale, используемый в OC.

Действительно, в экзешнике лежит честная Юникодная строка. Тогда зачем им локаль? Ведь код 0x40xx явно указывает на кириллицу. Вобщем стало еще мутнее.

K_O>А вот почему stream не может отображать такие строки без явного задания locale — это уже отдельный вопрос, какие-то их внутренние заморочки.


— Каа, и ты принимаешь бой...
— Пляяяяяя.......

K_O>Кстати, а какой locale установлен в ОС топикстартера? Если английский/американский — тогда можно объяснить такое поведение, если русский — тогда не знаю.


Вроде бы по старту экзешника ставится американская локаль всегда. Во всяком случае она возвращается по запросу локали.
Re[2]: тысяча второй раз про Юникод
От: quodum  
Дата: 13.10.09 15:30
Оценка: :))
Здравствуйте, pepsicoca, Вы писали:

P>Модифицировал пример и самостоятельно сформировал Юникодную строку L"привет" в виде массива.


[]

P>Этот пример НЕ печатает русский "привет".


Этот пример печатает русский "привет":
#include <iostream>

wchar_t array[]={0x043f,0x0440,0x0438,0x0432,0x0435,0x0442,0};

int wmain(int argc, wchar_t* argv[])
{
    std::wcout.imbue(std::locale("rus_rus.866"));
    std::wcout<<L"hello"<<std::endl;
    std::wcout<<array<<std::endl;
    return 0;
}


Этот пример печатает русский "привет":
#include <iostream>

#pragma setlocale( "rus_rus.1251" )

int wmain(int argc, wchar_t* argv[])
{
    std::wcout.imbue(std::locale("rus_rus.866"));
    std::wcout<<L"hello"<<std::endl;
    std::wcout<<L"Привет"<<std::endl;
    return 0;
}



P>Очевидно, что std::wcout действительно работает все-таки не с Юникодом,

Очевидно, что внимательнее надо быть.

P>2. А честный Юникодный исходник бывает?

Бывает. Создайте уникодный файл Блокнотом и подсуньте его Студии.

P>Спасибо.

Удачи.
Re: тысяча второй раз про Юникод
От: Vain Россия google.ru
Дата: 13.10.09 18:02
Оценка:
Здравствуйте, pepsicoca, Вы писали:

P>Есть MSVC2003 и есть код.

может ещё stlport проверить для сравнения?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re: тысяча второй раз про Юникод
От: _DAle_ Беларусь  
Дата: 13.10.09 18:24
Оценка:
Здравствуйте, pepsicoca, Вы писали:

P>Добрый день.


...

Почитай тут: http://rsdn.ru/forum/cpp/550632.all.aspx
Автор: Зверёк Харьковский
Дата: 25.02.04
Re[4]: тысяча второй раз про Юникод
От: Кодт Россия  
Дата: 14.10.09 08:44
Оценка:
Здравствуйте, pepsicoca, Вы писали:


P>1. Умеет ли редактор и транслятор MSVC работать в Юникодными исходниками? Если да, то где этот режим включается?


Да, умеет.
Компилятор смотрит на BOM-префикс текста и распознаёт UTF-8, UTF-16, UTF-16BE. Про UTF-32 не знаю, думаю, что не поймёт.
Редактор тоже смотрит на BOM. Смотри в диалоге Save As у кнопки Save[V] расширение, Save with encoding.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re[2]: тысяча второй раз про Юникод
От: alsemm Россия  
Дата: 14.10.09 09:14
Оценка:
Здравствуйте, pepsicoca, Вы писали:

P>1. Для китайского языка смещение гораздо больше, чем 128 символов из второй половины ASCII-таблицы. Как же в ASCIIшном исходнике задать китайскую строку, хотя бы и с помощью китайской локали?

Для китайского и им подобных языков, которым 128 символов мало, используются DBCS кодировки http://www.microsoft.com/typography/unicode/cs.htm
Re: тысяча второй раз про Юникод
От: alsemm Россия  
Дата: 14.10.09 09:31
Оценка:
Здравствуйте, pepsicoca, Вы писали:

P>Если закомментировать строчку std::wcout.imbue(std::locale("rus_rus.866"));, то русский "привет" не печатается.

Это от того, что все wZZZ функции из стандартных C/C++ библиотек это, грубо говоря, просто обертки над соответствующими функциями без префикса 'w', соответсвенно зависят от локали.

Отказаться от локали невозможно, т.к., например вывод
printf('%f\n', 3.14);

будет зависеть от локали (разделитель — точка или запятая).
Зачем указывать локаль при выводе на консоль? — чтобы дать хинт системе какие шрифты использовать для отображения символов. Ведь обычно шрифты делаются для определенного подмножества Unicode code point-ов.

Для полноценной работы с Unicode текстом средств стандартных C/C++ библиотек недостаточно, нужно использовать спец. библиотеки, ICU, например. Например tolower/toupper — locale specific, так что если вы получили произвольную unicode строку, tolower/toupper в общем случае к ней применять нельзя. А в ICU есть соответ. функции, которые от локали не зависят.
Re[8]: тысяча второй раз про Юникод
От: Кодт Россия  
Дата: 14.10.09 09:37
Оценка: 3 (1)
Здравствуйте, Kh_Oleg, Вы писали:

K_O>А вот почему stream не может отображать такие строки без явного задания locale — это уже отдельный вопрос, какие-то их внутренние заморочки.


Всё очень просто, на самом деле. Ну, почти просто.

1. Консольный вывод — (мульти)байтовый, а не вайдовый. Поэтому wcout должен выполнять преобразование wctomb.

2. По умолчанию окно консоли работает в кодировке "OEM" — 866 (Cyrillic DOS), 860 (Western DOS) и т.п. Это влияет на то, как она переводит байты, вылетающие из stdout программы, в отображаемые глифы шрифта. Надо заметить, что консоль способна поддерживать юникод (лишь бы шрифт его поддерживал). Но wcout на это непосредственно влиять не может, так же как на заголовок или размер консоли — для этого существует Console API (а что существует в *nix — не ведаю).

3. А вот теперь самое главное. Смотрим пункт 1. По умолчанию локаль программы — отнюдь не юзерская или системная, а "C".
Вот фигня-то и получилась.

Кардинальное лечение, кстати — это ::setlocale(LC_CTYPE, "") — т.е. установить юзерскую кодировку. Можно и явно указать "rus_rus.1251", и "rus_rus.866".
Это повлияет не только на wcout, но и на wprintf.
Другие фасеты можно оставить сишными — иначе получите внезапные артефакты в виде десятичной запятой вместо точки, и т.п.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.