UCS-4 в многобайтовое преобразование в Solaris
Почему этот код:
char a[10];
wchar_t w[10] = L"ä"; // German a Umlaut
int e = wcstombs(a, w, 10);
вернуть е == -1?
Я использую Oracle Solaris Studio 10 на Solaris 11. Локаль - Latin-1, которая содержит немецкие умлауты. Все документы, которые я нашел, указывают (для меня), что преобразование должно быть успешным.
Если я сделаю это:
char a[10] = "ä"; // German a Umlaut
wchar_t w[10];
int e = mbstowcs(w, a, 10);
e = wcstombs(a, w, 10);
ошибки нет, но результат неверный. (Какой-то вариант верхнего А.)
Я также попробовал wstostr с похожим результатом.
2 ответа
1) убедитесь, что правильное значение попадает в wchar_t. Компилятор, производящий строковый литерал широких символов, должен преобразовать L"ä"
от кодировки исходного кода до широкой кодировки исполнения.
2) проверьте правильность локали программы. Вы можете сделать это с printf("%s\n", setlocale(LC_ALL, NULL));
Я подозреваю, что проблема 1), потому что для меня, даже если языковой стандарт программы не установлен правильно, я все еще получаю ожидаемый результат. Чтобы избежать проблем с кодировкой исходного кода, вы можете экранировать не-ascii символы, такие как L"\x00E4"
,
1 #include <iostream>
2 #include <clocale>
3
4 int main () {
5 std::printf("%s\n", std::setlocale(LC_ALL, NULL)); // prints "C"
6
7 char a[10];
8 wchar_t w[10] = L"\x00E4"; // German a Umlaut
9 std::printf("0x%04x\n", (unsigned)w[0]); // prints "0x00e4"
10
11 std::setlocale(LC_ALL, "");
12 printf("%s\n", std::setlocale(LC_ALL, NULL)); // print something that indicates the encoding is ISO 8859-1
13 int e = std::wcstombs(a, w, 10);
14 std::printf("%i 0x%02x\n", e, (unsigned char)a[0]); // print "1 0xe4"
15 }
16
Наборы символов в программах на C и C++
В вашем исходном коде вы можете использовать любой символ из "исходного набора символов", который является надмножеством "основного исходного набора символов". Компилятор преобразует символы в строковых и символьных литералах из исходного набора символов в набор символов выполнения (или широкий набор символов выполнения для широких строк и литералов символов).
Проблема в том, что исходный набор символов зависит от реализации. Обычно компилятор просто должен знать, какую кодировку вы используете для исходного кода, и тогда он будет принимать любые символы из этой кодировки. GCC имеет аргументы командной строки для установки кодировки источника, Visual Studio будет предполагать, что источник находится в кодовой странице пользователя, если он не обнаруживает одну из так называемых подписей Unicode для UTF-8 или UTF-16, и Clang в настоящее время всегда использует UTF-8.
Как только компилятор использует правильный исходный набор символов для вашего кода, он будет генерировать строковые и символьные литералы в "наборе символов выполнения". Набор символов выполнения является еще одним расширенным набором базового исходного набора символов, а также зависит от реализации. GCC принимает аргумент командной строки для установки набора символов выполнения, VS использует локаль пользователя, а Clang использует UTF-8.
Поскольку исходный набор символов зависит от реализации, переносимый способ записи символов вне базового набора состоит в том, чтобы либо использовать шестнадцатеричное кодирование для непосредственного указания числовых значений, которые будут использоваться при выполнении, либо (если вы не используете C89/90) для используйте универсальные имена символов (UCN), которые преобразуются в набор символов выполнения (или широкий набор символов выполнения при использовании в широких строковых и символических литералах). UCN выглядят как \uNNNN или \UNNNNNNNN и задают символ из набора символов Unicode со значением кодовой точки NNNN или NNNNNNNN. (Обратите внимание, что C99 и C++11 запрещают вам использовать суррогатные кодовые точки, если вы хотите, чтобы символ находился за пределами BMP, просто напишите его значение, используя \U.)
Наборы исходных и исполняемых символов определяются во время компиляции и не меняются в зависимости от локали системы, в которой выполняется программа. То есть языковой стандарт программы использует другую кодировку, не обязательно совпадающую с набором символов выполнения. Однако широкий набор символов должен соответствовать кодировке широких символов, используемой поддерживаемыми локалями.
Поведение Solaris Studio
Компилятор Oracle для Solaris имеет очень простое поведение. Для узких строковых и символьных литералов конкретная исходная кодировка не указывается, байты из исходного кода просто используются непосредственно в качестве литерала выполнения. Это фактически означает, что набор символов выполнения совпадает с кодировкой исходных файлов. Для литералов широких символов исходные байты конвертируются с использованием системного языкового стандарта. Это означает, что вы должны сохранить исходный файл, используя кодировку локали, чтобы получить правильные широкие литералы.
Я подозреваю, что ваш исходный код сохраняется в кодировке, отличной от указанной в локали, поэтому вашему компилятору не удалось получить правильный широкий строковый литерал из L"ä"
, Ваш редактор может использовать UTF-8. Вы можете проверить, используя следующую программу.
1 #include <iostream>
2 #include <clocale>
3
4 int main () {
5 wchar_t w[10] = L"ä"; // German a Umlaut
6 std::printf("0x%04x 0x%04x\n", (unsigned)w[0], (unsigned)w[1]);
7 }
8
Так как wcstombs может правильно преобразовать широкий символ 0x00E4 в кодировку latin-1 'ä', вы хотите, чтобы выше отображалось 0x00E4 0x0000
, Если кодировка исходного кода UTF-8, вы должны увидеть 0x00C3 0x00A4
,
Возможно, вам придется установить язык, чтобы понимать немецкий язык. В частности, вы хотите фасет ctype.
Попробуй это:
setlocale( LC_ALL, ".1252" );
или конкретно это:
setlocale( LC_CTYPE, ".1252" );
Возможно, вам придется искать лучшую кодовую страницу, чем ".1252". Удачи.
Примеры кодовой страницы выше - Windows. В системах Unixy попробуйте "de_DE" для кодовой страницы.