Подтвердить что ты не робот

Как использовать Unicode в С++?

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

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

Это так просто, что это первое, что вы узнаете.

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

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

Спасибо.


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*'
4b9b3361

Ответ 1

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

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

и что

Ширина wchar_t зависит от компилятора и может быть как 8 бит. Вследствие этого, программы, которые должны быть переносимыми через любой компилятор C или С++, не должны использовать wchar_tдля сохранения текста в Юникоде. Тип 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 your name? ";
    std::string name;
    std::getline(std::cin, name);
    std::cout << "Hello there, " << name << "." << std::endl;
    return 0;
}

...

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

Но нет ничего Юникод об этом. Он просто читается в символах, которые входят в 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). Это barfing неправильных символов было вызвано Mojibake.

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

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

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

Ответ 2

Попробуйте заменить 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;
}

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

Ответ 3

#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;
}

Ответ 4

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

Ответ 5

Предпосылка: http://www.joelonsoftware.com/articles/Unicode.html

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

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

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

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

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

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

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

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

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

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

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

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

Что происходит с строковым завершающим байтом NULL?

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

Отказывает ли Visual С++ Debugger текст UNICODE?

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

Резюме:

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

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