Unicode in C++
Думаю, при помощи нормальных кросс-платформенных тулкитов вроде Qt и Gtk, которые уже внутри себя сами обрабатывают этот самый Юникод.
преобразование из wchar_t в байты внутри стрима идет наитупейшим образом.Программист Вася вручную переписывает STL.
Собственно вопрос: как нормальные люди обращаются с юникодом в кроссплатформенных приложениях написанных на С++?
Для начало нужно понять, с какой кодировкой ты работает, что есть юникод. Обычно сейчас называют юникодом кодировку UTF-8. приемущества — она однобайтная, недостатки — разные символы могут имать разную длинну.
Далее, нуджно понимать, что
в кроссплатформенных приложениях написанных на С++
меньше всего нужно думать о
msvcПотому что для винды придется писать отдельно, она не понимает utf-8 просто так.
Резюме:
1) определись, какое приложение ты пишешь — консольное, графическое или демон (службу)?
1а) в случае консоли нужно писать адаптер для вывода в консоль из utf-8
1б) в графическом — что ты будешь использовать GTK/Qt, что под виндой? что под Маком?
1в) демон пишет всё только в логи, поэтому проблем не будет.
Короче, уточняй задачу, и сразу совет — используй cmake. Он нормально работает с msvc, и всеми компиляторами.
Хм. Пожалуй изначальный вопрос стоит переформулировать - как вообще нормальные люди пишут кроссплатформенные приложения на С++?
Какие приложения? Примеры?
А вообще, судя по вопросам, ты нихуя не напишешь, лучше не берись, иди поучись писать у кого-нить.
Хм. Пожалуй изначальный вопрос стоит переформулировать - как вообще нормальные люди пишут кроссплатформенные приложения на С++?Без юникода пишут, пока нормально получается
Без русского вывода, правда)
Обычно сейчас называют юникодом кодировку UTF-8. приемущества — она однобайтная, недостатки — разные символы могут имать разную длинну.дык однобайтная или разной длины?
Ее главная фича — обратная совместимость с кучей Cишного хлама, афаик
я же не тебя подкалывал
версия msvc какая?
Для начало нужно понять, с какой кодировкой ты работает, что есть юникод. Обычно сейчас называют юникодом кодировку UTF-8. приемущества — она однобайтная, недостатки — разные символы могут имать разную длинну.Работаю с кучей кодировок, причем не обязательно юникодных. Минимальный необходимый набор - cp1251, UTF8, UTF16.
1) определись, какое приложение ты пишешь — консольное, графическое или демон (службу)?Консольное, ага.
1а) в случае консоли нужно писать адаптер для вывода в консоль из utf-8
Но я честно говоря не вижу особых преимуществ использования UTF8 как внутренней кодировки программы. Про адаптер - ну я уже написал его для вывода UTF16, легче мне от этого не стало - ощущение черезжопности не прошло ).
Какие приложения? Примеры?Самый тупой пример: консольный парсер HTML, который в HTML'ке ищет нечто, и выдает на stdout. Отсюда и необходимость работы с кодировками.
А вообще, судя по вопросам, ты нихуя не напишешь, лучше не берись, иди поучись писать у кого-нить.Хы, да я уже все написал, а теперь вот встал вопрос о прикручивании поддержки различных кодировок и вывода в UTF8/16.
http://icu-project.org/index.htmlХм, это чудо я уже ботал. IO API там противный, но конечно лучше чем ничего.
В общем, я хотел получить ответ вроде "ну нормальные люди юзают либы A, B и C и не парятся - их код компилится и одинаково работает (почти) везде". Для тяжеловесных GUI приложений ответ я получил. С легковесными видимо придется париться.
версия msvc какая?8.0
stlport кстати не помогает? или другие реализации stl-я?
Но я честно говоря не вижу особых преимуществ использования UTF8 как внутренней кодировки программы.Увидь. Подумай, почему люди используют эту кодировку, например, в интернете везде.
Самый тупой пример: консольный парсер HTML, который в HTML'ке ищет нечто, и выдает на stdout.Зачем там начинать с msvc?
Кстати, если производительность не важна — используй Mozilla для парсинга. Она выдает 16-битный юникод, на входе — все че хочешь. Более того, она имеет весь арсенал для работы с этими строками, подобие STL.
Но парсить больше 50 страниц в секунду ты её не заставишь.
Её обычно используют для передачи данных, а не для внутреннего хранения. Для внутреннего utf-16.
Потому что для винды придется писать отдельно, она не понимает utf-8 просто так.ХЗ насчёт utf-8, но разве ucs-2 она не понимает "просто так"? А что тогда она "просто так" понимает?
wcout.imbue(locale("rus_rus";
wcout << L"хфй" << endl;
работает под MSVC 7.1 нормально.
rus_rusА если хочется не "рус_рус", а часть символов - кирриллица, часть - западноевропейские, а часть - вообще иероглифы? Юникод вообще-то как раз для этого разрабатывался.
Ну это не ко мне, а ко врачу, который тебе лоботомию делал. Консоль в винде не может отображать символы всех локалей одновременно.
Консоль в винде не может отображать символы всех локалей одновременноЭто кто тебе такое сказал?
Если есть доказательства обратного, то о существовании оных нужно заявить тому, кто этот топик вообще открывал.
Что я делаю не так?
Даже если и так, что ты узнать-то хотело?
Я так понимаю, что проблема в том, что код, который ты будешь компилировать, получается не юникодный, поэтому запись L"[лол]" интерпретирует 'л' неправильно. Если избегать нелатинских строковых констант в коде, то всё должно работать.
Меняешь дефолтный шрифт, хуле.То есть, ты признаёшь, что это:
Консоль в винде не может отображать символы всех локалей одновременно- наглая ложь?
Потому что консоль в винде может отображать символы всех локалей одновременно, проблема в том, что в некоторых случаях дефолтный шрифт консоли - "точечные шрифты", а не юникодные Lucida Console или Consolas.
Я так понимаю, что проблема в том, что код, который ты будешь компилировать, получается не юникодный, поэтому запись L"[лол]" интерпретирует 'л' неправильно. Если избегать нелатинских строковых констант в коде, то всё должно работать.Если вбивать строковые константы циферками, или даже преобразовывать в рантайме из интов в массив wchar_t - получаю то же самое. Баг глобальный, и не у меня. Вечером попробую stlport - сейчас работаю с реализацией stl, что идет в комплекте со студией.
Консоль в винде не может отображать символы всех локалей одновременно.Какое вообще отношение к обсуждаемому вопросу имеет виндовая консоль? Не все ли равно, что она отображает, а что нет - все равно через пайп все можно считать в нужном для себя виде.
Н-да, с stlport все еще хуже. Мне его вообще не удалось заставить выводить UTF16 в wfstream. Ну и на консоль ес-но тоже ничего не выводится. Жесть короче.
Я так понимаю, что проблема в том, что код, который ты будешь компилировать, получается не юникодный, поэтому запись L"[лол]" интерпретирует 'л' неправильно. Если избегать нелатинских строковых констант в коде, то всё должно работать.Кстати, тогда к тебе вопрос - если у меня исходники в юникоде (UTF-8 что будет записано в строку в этом случае:
const char* str = "абра-кадабра"?
Символы будут по прежнему занимать один байт? Строка будет преобразована в однобайтную кодировку? Что произойдет, в общем?
23 байта будет записано.
Йопт. А если я потом в таком же виде выведу это на консоль, понимающую UTF-8, оно все нормально покажется по-русски?
именно!
ну и вообще управляющие символы игнорит. почему — хз, не вникал. если кто знает, где происходит игнор — скажите.
Кстати, тогда к тебе вопрос - если у меня исходники в юникоде (UTF-8 что будет записано в строку в этом случае:зависит от компилятора. и от того, можно ли объяснить в какой кодировке приходят исходные файлы, и в какой кодировке должны формироваться строки в exe.
const char* str = "абра-кадабра"?
зависит от компилятора. и от того, можно ли объяснить в какой кодировке приходят исходные файлы, и в какой кодировке должны формироваться строки в exe.каким образом это зависит от компилятора, что значит "нельзя объяснить в какой кодировке приходят исходные файлы" и что такое кодировка строк в exe?
Мож шибко умный компилятор от винды поймет, что это UTF-8, и в строку все как CP1251 загонит?
в общем случае, у тебя один cpp-ишник может быть в win-кодировке, другой - в utf-8, третий - в utf-16, четвертый - в koi8. Соответственно, компилятор может такие исходные файлы в разной кодировке поддерживать, а может и не поддерживать.
> что такое кодировка строк в exe
например, русский язык в однобайтной кодировке - может кодироваться в Dos-кодировке, может в win, а может и в nix-овой(koi8)
или проще говоря - буква 'ф' в одном случае будет записана как один байт(число а в другом будет записана как другой байт(другое число).
из второго твоего абзаца выходит, что некие особо умные компиляторы могут наплевать на кодировку исходника (т.е. кодировку описываемой строковой константы) и перекодировать его в свою любимую кодировку.
мне интересно, чем нужно руководствоваться, чтобы такое странное поведение оказалось оправданным?
не, ну насколько я понимаю, если строковая константа записана в исходнике с какой-то кодировкой, то это однозначно определяет последовательность байт, которая ей соответствует, и писать именно эту последовательность байт в скомпилированный код — имхо вполне здравая идеяЗависит от языка.
В XML, например, это, можно сказать, что и не так.
Это ты с той точки зрения смотришь, что ты в среде разработки работаешь, а не сам подсовываешь компилятору файлы.
мне кажется странным, что компилятор пытается что-то перекодировать, если я его об этом не просил.
Под винду:
Юзаем WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE)...) для вывода на консоль (если вывод программы не перенаправлен в пайп / файл WriteFile(GetStdHandle(STD_OUTPUT_HANDLE)...) - если перенаправлен. Что юзать определяем через isatty (по идее должна быть такая же ф-я из WinAPI, но я особо не искал). В итоге на консоли отображаются юникодные символы, а при перенаправлении вывод в файл идет в UTF16.
Под никсами:
Не паримся с wchar_t и юзаем char, считая что весь io идет в прозрачном UTF8. То есть можно спокойно юзать стримы или fwrite и не заморачиваться с isatty.
Для перекодировки используем MultiByteToWideChar / WideCharToMultiByte под виндой и iconv под никсами. Обертываем все это в удобоваримый интерфейс, и счастье наступает.
Итог: бесплатного решения не через жопу нет.
в каком смысле не так и что такое скомпилированный XML-код?
А то будет половина меню в одной кодировке, половина в другой
это же странно! =)
я в файле задал константу (последовательность байт а он ее меняет.
ему что, делать нефиг?
ну или не меняет, а говорит: "чувак, фиговая какая-то у тебя константа. пожалуй, я свалюсь (выдам предупреждение)."
В том смысле, что ты считываешь xml-файл, в котором все тексты в cp1251, и в метаданных сказано, что это cp1251 - и получаешь тексты во внутренней кодировке своей программы (например, utf-8 вообще не думая о кодировках.
Да задал ты константу в KOI8, а компилятору не сказал, что исходник в кодировке KOI8, и он по умолчанию в win ее и скомпилил. Откуда ему знать, что у тебя другое умолчание?
Да задал ты константу в KOI8, а компилятору не сказал, что исходник в кодировке KOI8, и он по умолчанию в win ее и скомпилил.А константа - это набор символов или набор байт?
Потому что у байт кодировок не бывает.
в исходном примере файл был в UTF-8, но последовательность байт в бинарнике почему-то зависела от компилятора.
объясни мне плз на пальцах, как мог получиться такой эффект? для меня это нечто сверхъестественное.
вот была буква "а", d0b0 в UTF-8. компилятор "по умолчанию" компилит ее в win. что будет в бинарнике?
Как ты думаешь сам, мы обсуждаем сейчас то у чего есть кодировка или то у чего нет кодировки?
А UTF8 вообще соберется, если компилятор будет про него думать что он win ?
У C++ есть кодировка?
Наоборот - не соберётся.
А в cp1251 всё допустимо.
а ты спрашиваешь о кодировке языка программирования.
у с++ - нет, у исходников есть.
Но если взгянуть на проблему с другой стороны - если у тебя разные исходные файлы в разных кодировках (о чем уже то среда тебе твои русские символы покажет везде нормально, а вот скомпилены будут разные строки.
Ну, для справки - после того, как я конвертил коды в UTF-8, у меня стал валиться тест на открытия файла с русским именем, потому что теперь конструтору fstream передается строка в другой кодировке. Это в msvc, в линуксе я не собирал еще.
Какой вариант тебя больше устроит? ) Я думаю, что это все полностью зависит от компилятора или от среды, или от компилятора+среда.
В том смысле, что ты считываешь xml-файл, в котором все тексты в cp1251, и в метаданных сказано, что это cp1251 - и получаешь тексты во внутренней кодировке своей программы (например, utf-8 вообще не думая о кодировках.это что за волшебство? опиши плз процесс считывания файла.
и заодно то, как с этим процессом связан язык программирования XML. =)
Вообще, по идее, ты должен быть прав - в файле записана последовательность байтов, которая должна быть загнана в строку независимо от того, в какой кодировке среда отображает твой файл.именно!
Но если взгянуть на проблему с другой стороны - если у тебя разные исходные файлы в разных кодировках (о чем уже писал то среда тебе твои русские символы покажет везде нормально, а вот скомпилены будут разные строки.жесть!
Ну, для справки - после того, как я конвертил коды в UTF-8, у меня стал валиться тест на открытия файла с русским именем, потому что теперь конструтору fstream передается строка в другой кодировке. Это в msvc, в линуксе я не собирал еще.
Я думаю, что это все полностью зависит от компилятора или от среды, или от компилятора+среда.жесть!
короче, я понял всё. спасибо участнегам за дискуссию.
опиши плз процесс считывания файлаНу ты суёшь парсеру xml, он его парсит и выдаёт тебе структуру в твоей рабочей кодировке, вне зависимости от того, какая там кодировка в самом xml использовалась.
Для перекодировки используем MultiByteToWideChar / WideCharToMultiByte под виндой и iconv под никсами.Обертываем все это в удобоваримый интерфейс, и счастье наступает.Что-то мешает использовать wcstombs и там, и там?
парсер != компилятор.
и парсер, и компилятор, по хорошему должны тебе результат парсинга отдать, а перекодировать
в нужный тебе формат для обработки (руководствуясь информацией о кодировке xml-файла) — твоя задача.
твой парсер xml умеет выполнять некоторые твои задачи за тебя.
Ну объясни тогда, что такое парсер, что такое компилятор, и что играет роль парсера, а что - компилятора в случае xml-файлов
аналог с компилятором: вывод на консоль строковой константы, зашитой в код.
ты должен знать в какой кодировке выводишь и сказать об этом консоли, чтобы она поняла, как это читать.
если ты в какой-нибудь переменной будешь хранить кодировку вывода, задаваемую из, например, конфига,
а компилятор все твои строки в разных кодировках сконвертит в одну, случится страшное.
твой парсер перекодирует xml потому, что ему об этом сказали. так же, как и консоли в примере выше.
Что-то мешает использовать wcstombs и там, и там?Есть предположение, что смена глобальной локали, на основе которой wcstombs конвертирует, будет давать тормоза и проблемы в мультитредовых приложениях.
Под виндой еще есть wcstombs_l, которому локаль передается непосредственно, но man(3) про эту ф-ю не знает, так что не факт, что она будет везде доступна. Я правда особо не тестил, только в man глянул.
Есть предположение, что смена глобальной локали, на основе которой wcstombs конвертирует, будет давать тормоза и проблемы в мультитредовых приложениях.Есть предположение, что кривизна рук передается непосредственно в мозг.
не, ну насколько я понимаю, если строковая константа записана в исходнике с какой-то кодировкой, то это однозначно определяет последовательность байт, которая ей соответствует, и писать именно эту последовательность байт в скомпилированный код — имхо вполне здравая идея.это все клево, но как быть если у тебя в исходнике двухбайтная кодировка, а в exe-шнике однобайтная, или наоборот?
Есть предположение, что кривизна рук передается непосредственно в мозг.Ты наверно тут самый умный, да?
Давай, дерзай, напиши ф-ю конвертации из произвольной кодировки в utf16 с использованием mbstowcs, которая будет нормально работать в мультитредовом приложении. Ты наверно собрался перед каждым вызовом mbstowcs лочить мутекс, а затем вызывать setlocale - mbstowcs - setlocale? Такой подход убивает всю пользу от мультитредовости, плюс небезопасен, т.к. пользователю ф-ии надо знать, что отныне ему самому нельзя вызывать setlocale.
The function _configthreadlocale is used to control whether setlocale affects the locale of all threads in a program or only the locale of the calling thread.
как быть если у тебя в исходнике двухбайтная кодировка, а в exe-шнике однобайтная, или наоборотА нет кодировок, строки - это последовательности байт.
Пусть у тебя есть две функция
int checkPass (const char * pass)
{
return strcmp(pass, "пассворд");
}
доставшаяся в наследство от старого мудрого хакера и лежащая в DOS-кодировке. И новая функция, точно такая же, но уже в файлеге, закодированном win1251. Когда ты открываешь оба файлега в ИДЕ, она понимает, о какой кодировке идёт речь и отображает их одинаково (допустим). Ты их компилишь и... И? У тебя получаются две разные функции? Хотя визуально они одинаковы? Или как?
Ты, конечно, можешь сказать, что это самоуправство со стороны IDE. А что делать с тремя файлегами в utf8, unicode (то есть UCS-2, насколько я понимаю) и utf16 (ну там, допустим, ещё иероглифы какие-нибудь есть кроме русской записи, которые как-нибудь влияют)?
Вот и получается, что вообще говоря компилятору нужно быть довольно-таки умным и самостоятельно (однако взаимодействуя со средой) периодически преобразовывать кодировки. Например, если ты пишешь
wchar * s = TEXT("пассворд");
то компилятор должен вне зависимости от кодировки файла создать строковую константу со словом "пассворд" в UCS-2.
Ты, конечно, можешь сказать, что это самоуправство со стороны IDE.Я так и скажу.
А что делать с тремя файлегами в utf8, unicode (то есть UCS-2, насколько я понимаю) и utf16Пытаться отобразить их в той кодировке, которая в настройках.
Для языка программирования, имхо - лучший вариант. А у XML кодировка определяется не каким-то непонятным образом, а очень даже реальным заголовком.
это все клево, но как быть если у тебя в исходнике двухбайтная кодировка, а в exe-шнике однобайтная, или наоборот?записать в экзешник (количество символов * 2) байт? =)
я просто все еще не понимаю, что такое кодировка exe-шника.
в начальных условиях уже есть какой-то exe-шник, который собирали из однобайтных исходников? или как?
Это все конечно замечательно, но эта ф-я есть только под виндой (
записать в экзешник (количество символов * 2) байт? =)какой символ тогда будет записан в переменную ch?
char ch = "Вася"[0]
так зачем придумали писать букву L перед строковой константной?
hint:
это появилось - когда исходники обычно были в однобайтной кодировке, а exe-шники понадобились с строковыми двухбайтными константами
какой символ тогда будет записан в переменную ch?если исходник в UTF-8, то 00.
char ch = "Вася"[0]
так зачем придумали писать букву L перед строковой константной?я не догадаюсь всё равно, потому что у меня в голове эта ересь не укладывается! =)
hint:
это появилось - когда исходники обычно были в однобайтной кодировке, а exe-шники понадобились с строковыми двухбайтными константами
рискну предположить, что им лень было сконвертить исходник, но очень хотелось придумать новую крутую букву. =)
какой символ тогда будет записан в переменную ch?Если считать, что char - байт, а "Вася" - последовательность байт, то первый байт "Вася" в таком виде, в каком оно хранится в исходниках.
char ch = "Вася"[0]
и что это за символ? речь идет о том, что хотелось получить именно символ, а не байт.
и как с таким подходом - хотя бы написать программу, которая будет из константной строки выбирать только цифры?
const char *str = "абра-кадабра 666";
for (; *str; ++str) if ('0' <= *str && *str <= '9') // ...
если надо искать не ASCII, можно написать многобайтный strchr. не вижу проблемы.
при считывании с произвольного устройства — конвертить во внутреннюю кодировку (хоть многобайтную, strchr-то написали).
цифры — это ж ASCII.Вообще-то, цифра в какой-то кодировке может записываться не только не своим ascii-кодом, но и вообще не содержать в этой записи ascii-код.
Я уж не говорю про то, что, если мы увидели байт с кодом, как у какой-то цифры в ascii - это необязательно эта цифра (см.UCS-2)
btw, мой второй абзац эту проблему решает.
тебе кто это сказал?
const char *str = "абра-кадабра 666";
for (; *str; ++str) if ('0' <= *str && *str <= '9') // ...
если строка была двухбайтная, то этот код обработает ровно одну цифру, потому что код 6 будет 0x36 0x00, а 0x00 - это конец строки
for (; *str; ++str)А слона-то я и не заметил...
В си ведь вроде были какие-то костыли для строк, содержащих нулевые байты?
если строка была двухбайтная, то этот код обработает ровно одну цифру,а что это за страшная кодировка, в которой так?
потому что код 6 будет 0x36 0x00, а 0x00 - это конец строки
UCS2 LE?
спасибо тебе и DG за разъяснения. =)
moo:~$ heump test
0000000 0a36
0000002
moo:~/subver/endeavour$ iconv -f utf8 -t utf16 test > test2
moo:~/subver/endeavour$ iconv -f utf8 -t ucs2 test > test3
moo:~/subver/endeavour$ heump test2
0000000 feff 0036 000a
0000006
moo:~/subver/endeavour$ heump test3
0000000 0036 000a
0000004
moo:~/subver/endeavour$ cat test
6
круто, да. =)
Оставить комментарий
tima56
Собственно вопрос: как нормальные люди обращаются с юникодом в кроссплатформенных приложениях написанных на С++?А то мне похоже уже пора головой об стену биться - если даже такое под виндой + msvc не работает:
wcout << L"[лол]" << endl;
Выводится первая скобка, после чего стрим падает в fail. Как выяснилось после ковыряний, преобразование из wchar_t в байты внутри стрима идет наитупейшим образом. Для того, чтобы заставить это все работать, приходится делать imbue локали с самописным codecvt в wcout. Короче, все через жопу.
И это не единственное место, где все через жопу ).
Хотелось бы простого и понятного солюшена, типа того, что есть в java.
Хм. Пожалуй изначальный вопрос стоит переформулировать - как вообще нормальные люди пишут кроссплатформенные приложения на С++?