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" для кодовой страницы.

Другие вопросы по тегам