Вопрос:
Мне нужно напечатать некоторые символы юникода на терминале Linux, используя iostream. Странные вещи случаются. Когда я пишу:
cout << «u2780»;
Я получаю: ➀, что почти то, что я хочу. Однако, если я пишу:
cout << ‘u2780’;
Я получаю: 14851712.
Проблема в том, что я не знаю, какой именно символ печататься во время компиляции. Поэтому я хотел бы сделать что-то вроде:
int x; // some calculations… cout << (char)(‘u2780’ + x);
Какие печатает: �. Использование wcout или wchar_t вместо этого также не работает. Как получить правильную печать?
Из того, что я нашел в Интернете, кажется важным, что я использую компилятор g++ 4.7.2 прямо из репозитория Debian Wheezy.
Ответ №1
Символ Unicode u2780 находится за пределами диапазона для типа данных char. Вы должны были получить это предупреждение компилятора, чтобы рассказать вам об этом: (по крайней мере, мой g++ 4.7.3 дает его)
test.cpp:6:13: warning: multi-character character constant [-Wmultichar]
Если вы хотите работать с такими символами, как U + 2780, как отдельные единицы, вам придется использовать широкоформатный тип данных wchar_t, или если вам посчастливилось работать с С++ 11, char32_t или char16_t. Обратите внимание, что одного 16-битного блока недостаточно для представления всего диапазона символов Юникода.
Если это не работает для вас, возможно, потому, что локаль по умолчанию “C” не поддерживает вывод не-ASCII. Чтобы устранить эту проблему, вы можете вызвать setlocale в начале программы; таким образом вы можете выводить полный диапазон символов, поддерживаемых языковой версией пользователя: (который может содержать или не иметь поддержки для всех символов, которые вы используете)
#include <clocale> #include <iostream> using namespace std; int main() { setlocale(LC_ALL, «»); wcout << L’u2780′; return 0; } Ответ №2
Когда вы пишете
cout << «u2780»;
Компилятор преобразует u2780 в соответствующую кодировку этого символа в наборе символов выполнения. Вероятно, это UTF-8, и поэтому строка заканчивается четырьмя байтами (три для символа, один для нулевого терминатора).
Если вы хотите сгенерировать символ во время выполнения, вам нужно как-то сделать во время выполнения такое же преобразование в UTF-8, что и во время компиляции.
С++ 11 предоставляет удобный wstring_convert шаблон и кодеки codecvt, которые могут это сделать, однако libstdС++, стандартная реализация библиотеки, поставляемая с gcc, еще не успела их реализовать (начиная с gcc 4.8). Ниже показано, как использовать эти функции, но вам нужно либо использовать другую стандартную библиотечную реализацию, либо дожидаться, когда libstdС++ сможет их реализовать.
#include <codecvt> int main() { char32_t base = U’u2780′; std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert; std::cout << convert.to_bytes(base + 5) << ‘n’; }
Вы также можете использовать любой другой способ создания UTF-8, который у вас есть. Например, iconv, ICU и ручное использование граней preec С++ 11 codecvt_byname будут работать. (Я не показываю этих примеров, потому что этот код будет более сложным, чем простой код, разрешенный wstring_convert.)
Альтернативой, которая будет работать для небольшого числа символов, было бы создание массива строк с использованием литералов.
char const *special_character[] = { «u2780», «u2781», «u2782», «u2783», «u2784», «u2785», «u2786», «u2787», «u2788», «u2789» }; std::cout << special_character[i] << ‘n’; Ответ №3
Программа печатает целое число из-за С++ 11 §2.14.3/1:
Многосимвольный литерал или литерал обычного символа, содержащий единственный c- char, не представимый в наборе символов выполнения, условно поддерживается, имеет тип int и имеет значение, определенное реализацией.
Набор символов выполнения – это то, что char может представлять, т.е. ASCII.
У вас есть 14851712 или в шестнадцатеричном e29e80, представляющем UTF-8 представление U + 2780. Помещение UTF-8, многобайтовой кодировки, в int является безумным и глупым, но это то, что вы получаете от “условно поддерживаемой, определенной реализацией” функции.
Чтобы получить значение UTF-32, используйте U’u2780′. Первый U указывает тип char32_t и кодировку UTF-32 (т.е. До 31 бит, но не суррогатные пары). Второй u указывает имя универсального символа, содержащее кодовую точку. Чтобы получить значение, предположительно совместимое с wcout, используйте L’u2780′, но это не обязательно использует значение времени выполнения Unicode и не может содержать более двух байтов памяти.
Что касается надежного манипулирования и печати кода кодировки Unicode, как уже отмечали другие ответы, стандарт С++ еще не дошел до него. Ответ Joni – лучший способ, но он все же предполагает, что компилятор и пользовательская среда используют один и тот же язык, что часто бывает неверным.
Вы также можете указать строки UTF-8 в источнике с помощью u8″u2780″ и заставить среду выполнения UTF-8 использовать что-то вроде std::locale::global( std::locale( «en_US.UTF-8» ) );. Но это все еще имеет грубые края. Joni предлагает использовать интерфейс C std::setlocale из <clocale> вместо С++-интерфейса std::locale::global из <locale>, что является обходным путем для интерфейса С++, который сломан в GCC на OS X и, возможно, на других платформах. Эти проблемы достаточно чувствительны к платформе, так что ваш дистрибутив Linux мог бы добавить патч в свой собственный пакет GCC.
Ответ №4
В Linux я успешно распечатывал любой юникод напрямую, как в самом наивном виде:
std::cout << «ΐ , Α, Β, Γ, Δ, ,Θ , Λ, Ξ, … ±, … etc»