Как использовать Unicode в C ++? - PullRequest
27 голосов
/ 10 июня 2010

Предполагая очень простую программу, которая:

  • спроси имя.
  • сохранить имя в переменной.
  • отображать переменное содержимое на экране.

Это так просто, это первое, чему человек учится.

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

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

Спасибо.


user362981: Спасибо за вашу помощь. Я скомпилировал код, который вы написали без проблем, в них появляется окно консоли, и я не могу ввести на нем японские символы (используя IME). Также если Я изменил слово в вашем коде ("привет") на слово, содержащее японские символы, оно также не будет отображаться.

Svisstack: Также спасибо за вашу помощь. Но когда я компилирую ваш код, я получаю следующую ошибку:

warning: deprecated conversion from string constant to 'wchar_t*'
error: too few arguments to function 'int swprintf(wchar_t*, const wchar_t*, ...)'
error: at this point in file
warning: deprecated conversion from string constant to 'wchar_t*'

Ответы [ 5 ]

37 голосов
/ 11 июня 2010

Вы получите много ответов о широких символах.Широкие символы, в частности wchar_t , не равны Unicode .Вы можете использовать их (с некоторыми ловушками) для хранения Unicode, так же, как вы можете unsigned char.wchar_t чрезвычайно зависит от системы.Цитируя стандарт Unicode, версия 5.2, глава 5:

С широким типом символов wchar_t ANSI / ISO C предусматривает включение широких символов фиксированной ширины,ANSI / ISO C оставляет семантику набора широких символов для конкретной реализации, но требует, чтобы символы из переносимого набора выполнения C соответствовали их эквивалентам широких символов при нулевом расширении.

и что

Ширина wchar_t зависит от компилятора и может составлять до 8 бит.Следовательно, программы, которые должны быть переносимы на любой компилятор C или C ++, не должны использовать wchar_t для хранения текста Unicode.Тип wchar_t предназначен для хранения определенных компилятором широких символов, которые могут быть символами Unicode в некоторых компиляторах.

Итак, его реализация определена.Вот две реализации: В Linux wchar_t имеет ширину 4 байта и представляет текст в кодировке UTF-32 (независимо от текущей локали).(Либо BE, либо LE в зависимости от вашей системы, в зависимости от того, какая система является родной.) Однако Windows имеет ширину 2 байта wchar_t и представляет вместе с ними кодовые единицы UTF-16.Совсем другое.

Лучший путь: узнайте о локалях, так как вам нужно это знать.Например, поскольку у меня настроена среда для использования UTF-8 (Unicode), следующая программа будет использовать Unicode:

#include <iostream>

int main()
{
    setlocale(LC_ALL, "");
    std::cout << "What's your name? ";
    std::string name;
    std::getline(std::cin, name);
    std::cout << "Hello there, " << name << "." << std::endl;
    return 0;
}

...

$ ./uni_test
What's your name? 佐藤 幹夫
Hello there, 佐藤 幹夫.
$ echo $LANG
en_US.UTF-8

Но в этом нет ничего Unicode.Он просто читает символами, которые обозначаются как UTF-8 , потому что моя среда настроена таким образом .С таким же успехом я мог бы сказать: «Черт, я частично чешский, давайте использовать ISO-8859-2»: Внезапно программа получает ввод в ISO-8859-2, но, поскольку она просто извергается, это не имеет значения, программа все равно будет работать правильно.

Теперь, если этот пример прочитал мое имя, а затем попытался записать его в файл XML и тупо записать <?xml version="1.0" encoding="UTF-8" ?> вверху, это было быправильно, когда мой терминал был в UTF-8, но неправильно, когда мой терминал был в ISO-8859-2.В последнем случае потребуется преобразовать его перед сериализацией в файл XML.(Или просто напишите ISO-8859-2 в качестве кодировки для XML-файла.)

Во многих системах POSIX текущим языковым стандартом обычно является UTF-8, поскольку он предоставляет пользователю несколько преимуществ, но этоне гарантируетсяПросто вывод UTF-8 в stdout обычно будет правильным, но не всегда.Скажем, я использую ISO-8859-2: если вы бездумно выведите ISO-8859-1 "è" (0xE8) на мой терминал, я увижу "č" (0xE8).Аналогично, если вы выводите UTF-8 "è" (0xC3 0xA8), я буду видеть (ISO-8859-2) "è" (0xC3 0xA8).Это искажение неправильных символов называлось Моджибаке .

Часто вы просто перетасовываете данные, и это не имеет большого значения.Это обычно вступает в игру, когда вам нужно сериализовать данные.(Например, во многих интернет-протоколах используется UTF-8 или UTF-16: если вы получили данные из терминала ISO-8859-2 или из текстового файла, закодированного в Windows-1252, вам придется преобразовать его, или выотправлять Mojibake .)

К сожалению, речь идет о состоянии поддержки Unicode, как в C, так и в C ++.Вы должны помнить: эти языки действительно не зависят от системы и не связаны с каким-либо конкретным способом сделать это.Это включает в себя наборы символов.Однако существуют тонны библиотек для работы с Unicode и другими наборами символов.

В конце концов, на самом деле все не так сложно: знайте, в какой кодировке находятся ваши данные, и знайте, в какой кодировке выходной код.должно быть. Если они не совпадают, вам нужно сделать преобразование.Это применимо, используете ли вы std::cout или std::wcout.В моих примерах stdin или std::cin и stdout / std::cout иногда были в UTF-8, иногда ISO-8859-2.

1 голос
/ 10 июня 2010
#include <stdio.h>
#include <wchar.h>

int main()
{
    wchar_t name[256];

    wprintf(L"Type a name: ");
    wscanf(L"%s", name);

    wprintf(L"Typed name is: %s\n", name);

    return 0;
}
1 голос
/ 10 июня 2010

Вы можете делать простые вещи с помощью универсальной поддержки широких символов в выбранной вами ОС, но обычно C ++ не имеет хорошей встроенной поддержки Unicode, так что в конечном итоге вам будет лучше, если вы посмотрите на что-то вроде ICU .

1 голос
/ 10 июня 2010

Попробуйте заменить cout на wcout, cin - на wcin, а строку - на wstring. В зависимости от вашей платформы это может работать:

#include <iostream>
#include <string>

int main() {
  std::wstring name;
  std::wcout << L"Enter your name: "; 
  std::wcin >> name;
  std::wcout << L"Hello, " << name << std::endl;
}

Существуют и другие способы, но это своего рода ответ "минимального изменения".

0 голосов
/ 11 мая 2016

Предварительные условия: http://www.joelonsoftware.com/articles/Unicode.html

Приведенная выше статья является обязательной для прочтения, которая объясняет, что такое юникод, но остается мало вопросов.Да, UNICODE имеет уникальную кодовую точку для каждого символа на каждом языке, и, кроме того, они могут быть закодированы и сохранены в памяти потенциально иначе, чем фактический код.Таким образом, мы можем сэкономить память, например, используя кодировку UTF-8, что замечательно, если поддерживаемый язык - только английский, и поэтому представление памяти по существу такое же, как ASCII - это, конечно, знание самой кодировки.Теоретически, если мы знаем кодировку, мы можем хранить эти более длинные символы Юникода так, как нам нравится, и читать их обратно.Но реальный мир немного отличается.

Как сохранить символ / строку в кодировке UNICODE в программе на C ++?Какую кодировку вы используете?Ответ заключается в том, что вы не используете какую-либо кодировку, но непосредственно сохраняете кодовые точки UNICODE в строке символов Unicode, так же как вы сохраняете символы ASCII в строке ASCII.Вопрос в том, какой размер символа следует использовать, поскольку символы UNICODE не имеют фиксированного размера.Простой ответ заключается в том, что вы выбираете размер символа, который достаточно широк, чтобы в нем содержалась самая высокая кодовая точка (язык) символов, которую вы хотите поддерживать.

Теория о том, что символ UNICODE может занимать 2 байта или более, все еще верна иэто может создать некоторую путаницу.Разве мы не должны хранить кодовые точки в 3 или 4 байтах, чем то, что действительно представляет все символы Юникода?Почему Visual C ++ хранит Unicode в wchar_t, то есть всего 2 байта, явно недостаточно для хранения каждой кодовой точки UNICODE?

Причина, по которой мы храним кодовую точку UNICODE в 2 байта в Visual C ++, фактически одинаковапричина, по которой мы хранили символ ASCII (= английский) в один байт.В то время мы думали только об английском, так что одного байта было достаточно.Сейчас мы думаем о большинстве международных языков, но не обо всех, поэтому мы используем 2 байта, что достаточно.Да, это правда, что это представление не позволит нам представлять те кодовые точки, которые занимают 3 байта или более, но мы пока не заботимся о них, потому что эти люди еще даже не купили компьютер.Да, мы не используем 3 или 4 байта, потому что мы все еще скупы на память, зачем хранить лишний 0 (нулевой) байт с каждым символом, когда мы никогда не собираемся его использовать (этот язык).Опять же, это те же самые причины, по которым ASCII хранил каждый символ в одном байте, зачем хранить символ в 2 или более байтов, когда английский можно представить в одном байте, и место, которое можно выделить для этих дополнительных специальных символов!

В теории 2 байта недостаточно для представления каждой кодовой точки Unicode, но этого достаточно для хранения всего, что нас может волновать сейчас.Истинное строковое представление UNICODE может хранить каждый символ в 4 байта, но нам просто нет дела до этих языков.

Представьте себе 1000 лет спустя, когда мы найдем дружественных инопланетян в изобилии и захотим общаться с ними, используя ихбесчисленные языки.Размер единого символа Юникода увеличится, возможно, до 8 байтов, чтобы вместить все их кодовые точки.Это не значит, что мы должны начать использовать 8 байтов для каждого символа Юникода сейчас.Память является ограниченным ресурсом, мы выделяем то, что нам нужно.

Могу ли я обрабатывать строку UNICODE как строку стиля C?

В C ++ строки ASCII все еще можно обрабатывать вC ++, и это довольно часто, захватывая его указателем char *, где можно применять функции C.Однако применение текущих строковых функций в стиле C к строке UNICODE не будет иметь никакого смысла, поскольку в ней может быть один NULL-байт, заканчивающий строку C.

UNICODE string больше не является простым буфером текста, но теперь он более сложен, чем поток однобайтовых символов, заканчивающийся байтом NULL. Этот буфер может обрабатываться указателем даже в C, но для этого потребуются вызовы, совместимые с UNICODE, или библиотека C, которая могла бы читать и записывать эти строки и выполнять операции.

Это упрощается в C ++ благодаря специализированному классу, который представляет строку UNICODE. Этот класс обрабатывает сложность буфера строки Unicode и обеспечивает простой интерфейс. Этот класс также решает, является ли каждый символ строки Unicode 2 байтами или более - это детали реализации. Сегодня он может использовать wchar_t (2 байта), но завтра он может использовать 4 байта для каждого символа для поддержки более (менее известного) языка. Вот почему всегда лучше использовать TCHAR, чем фиксированный размер, который соответствует нужному размеру при изменении реализации.

Как индексировать строку UNICODE?

Стоит также отметить, особенно при обработке строк в стиле C, что они используют индекс для обхода или поиска подстроки в строке. Этот индекс в строке ASCII непосредственно соответствует позиции элемента в этой строке, но он не имеет значения в строке UNICODE и его следует избегать.

Что происходит со строкой, заканчивающейся нулевым байтом?

Строки UNICODE по-прежнему завершаются байтом NULL? Достаточно ли одного NULL-байта для завершения строки? Это вопрос реализации, но NULL-байт по-прежнему является одной кодовой точкой Юникода и, как и любая другая кодовая точка, он все равно должен иметь такой же размер, как и любой другой (особенно если кодировка отсутствует). Таким образом, символ NULL также должен составлять два байта, если реализация Unicode-строки основана на wchar_t. Все кодовые точки UNICODE будут представлены одинаковым размером независимо от того, является ли он нулевым байтом или любым другим.

Показывает ли отладчик Visual C ++ текст UNICODE?

Да, если текстовый буфер имеет тип LPWSTR или любой другой тип, поддерживающий UNICODE, Visual Studio 2005 и более поздние версии, отображающий международный текст в окне просмотра отладчика (при условии, конечно, что установлены шрифты и языковые пакеты).

Резюме:

C ++ не использует какую-либо кодировку для хранения символов Юникода, но он напрямую сохраняет кодовые точки UNICODE для каждого символа в строке. Он должен выбрать размер символа, достаточно большой, чтобы вместить самый большой символ желаемых языков (свободно говоря), и этот размер символа будет фиксированным и использоваться для всех символов в строке.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...