Спрошу еще раз, потому что в старой ветке никто не читает.
Есть 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"привет"
он должен в объектнике создать Юникодную строку с русскими буквами. А дальше работать с Юникодом без указания на локаль.
Здравствуйте, Сергей Мухин, Вы писали:
СМ>Здравствуйте, pepsicoca, Вы писали:
P>>он должен в объектнике создать Юникодную строку с русскими буквами. А дальше работать с Юникодом без указания на локаль.
СМ>Откуда компилятор знает, что они русские?
СМ>#pragma setlocale( "[locale-string]" )
Очевидно, от редактора? В любом случае imbue это механизм времени исполнения, а к этому времени Юникодная строка
P>>
Здравствуйте, Сергей Мухин, Вы писали:
СМ>Откуда компилятор знает, что они русские?
Ну когда спрашивающий раскомментирует (в смысле раскомментировывает ) std::wcout.imbue(std::locale("rus_rus.866")), то "привет" по-видимому печатается, так что проблема не в том, что компилятор не знает, руские там буквы или нет.
СМ>>Откуда компилятор знает, что они русские?
СМ>>#pragma setlocale( "[locale-string]" )
P>Очевидно, от редактора? В любом случае imbue это механизм времени исполнения, а к этому времени Юникодная строка
Ну да, от ASCIIшного редактора компилятор это знать не может. То есть если по-честному, то программы с использованием Юникода должны набираться в Юникодном редакторе, а транслятор должен уметь транслировать Юникодные исходники.
Вопрос:
1. Умеет ли редактор и транслятор MSVC работать в Юникодными исходниками? Если да, то где этот режим включается?
Здравствуйте, 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.
Здравствуйте, Kh_Oleg, Вы писали:
K_O>Если указана русская locale, эти числа будут конвертированы в Unicode диапазон 0400h-04FFh, если мы, скажем, укажем японскую locale, то эти же числа из исходного текста будут конвертированы в другой диапазон кодов Unicode — 3040h-30FFh.
Ты не хочешь ли сказать, что выполняемое на этапе компиляции преобразование зависит от того, закоментирована ли std::wcout.imbue(std::locale("rus_rus.866"))?
Это юникодный русский L"привет", написанный мной в кодах и с нулем на конце.
Этот пример НЕ печатает русский "привет".
Очевидно, что std::wcout действительно работает все-таки не с Юникодом, а с локалью+код смещения из второй половины таблицы. Но при этом появляются новые вопросы:
Вопросы:
1. Для китайского языка смещение гораздо больше, чем 128 символов из второй половины ASCII-таблицы. Как же в ASCIIшном исходнике задать китайскую строку, хотя бы и с помощью китайской локали?
2. А честный Юникодный исходник бывает? И как тогда работает std::wcout? Или у честного Юникодного исходника и реализация библиотеки std::wcout другая и эта другая реализация библиотеки тоже честно работает с Юникодом, а не с локалью?
Здравствуйте, igna, Вы писали:
I>Здравствуйте, Kh_Oleg, Вы писали:
K_O>>Если указана русская locale, эти числа будут конвертированы в Unicode диапазон 0400h-04FFh, если мы, скажем, укажем японскую locale, то эти же числа из исходного текста будут конвертированы в другой диапазон кодов Unicode — 3040h-30FFh.
I>Ты не хочешь ли сказать, что выполняемое на этапе компиляции преобразование зависит от того, закоментирована ли std::wcout.imbue(std::locale("rus_rus.866"))?
Мне кажется, что преобразование происходит не на этапе компиляции.
Здравствуйте, 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?
Здравствуйте, igna, Вы писали:
I>Здравствуйте, Kh_Oleg, Вы писали:
K_O>>Мне кажется, что преобразование происходит не на этапе компиляции.
I>Да? А в исполняемом файле его L"привет" как хранится? Прямо так как написано, безо всякого преобразования в коды?
Как оказалось, в исполняемом файле хранится правильная Unicode строка. Очевидно, компилятор использует текущий locale, используемый в OC.
А вот почему stream не может отображать такие строки без явного задания locale — это уже отдельный вопрос, какие-то их внутренние заморочки.
Кстати, а какой locale установлен в ОС топикстартера? Если английский/американский — тогда можно объяснить такое поведение, если русский — тогда не знаю.
Здравствуйте, 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 установлен в ОС топикстартера? Если английский/американский — тогда можно объяснить такое поведение, если русский — тогда не знаю.
Вроде бы по старту экзешника ставится американская локаль всегда. Во всяком случае она возвращается по запросу локали.
P>Очевидно, что std::wcout действительно работает все-таки не с Юникодом,
Очевидно, что внимательнее надо быть.
P>2. А честный Юникодный исходник бывает?
Бывает. Создайте уникодный файл Блокнотом и подсуньте его Студии.
P>Спасибо.
Удачи.
P>1. Умеет ли редактор и транслятор MSVC работать в Юникодными исходниками? Если да, то где этот режим включается?
Да, умеет.
Компилятор смотрит на BOM-префикс текста и распознаёт UTF-8, UTF-16, UTF-16BE. Про UTF-32 не знаю, думаю, что не поймёт.
Редактор тоже смотрит на BOM. Смотри в диалоге Save As у кнопки Save[V] расширение, Save with encoding.
Здравствуйте, pepsicoca, Вы писали:
P>1. Для китайского языка смещение гораздо больше, чем 128 символов из второй половины ASCII-таблицы. Как же в ASCIIшном исходнике задать китайскую строку, хотя бы и с помощью китайской локали?
Для китайского и им подобных языков, которым 128 символов мало, используются DBCS кодировки http://www.microsoft.com/typography/unicode/cs.htm
Здравствуйте, 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 есть соответ. функции, которые от локали не зависят.
Здравствуйте, 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.
Другие фасеты можно оставить сишными — иначе получите внезапные артефакты в виде десятичной запятой вместо точки, и т.п.