WChars , Кодировки, стандарты и переносимость

Следующее не может рассматриваться как вопрос SO; если это выходит за рамки, пожалуйста, не стесняйтесь сказать мне, чтобы я ушел. В основном вопрос заключается в следующем: «Правильно ли я понимаю стандарт C и правильно ли это?»

Я хотел бы попросить разъяснений, подтверждения и исправлений в моем понимании обработки символов в C (и таким образом, C ++ и C ++ 0x). Прежде всего, важное наблюдение:

Переносимость и сериализация - ортогональные концепции.

Переносимые вещи - это такие вещи, как C, unsigned int , wchar_t . Сериализуемые вещи - это такие вещи, как uint32_t или UTF-8. «Переносимость» означает, что вы можете перекомпилировать один и тот же исходный код и получить рабочий результат на каждой поддерживаемой платформе, но двоичное представление может быть совершенно другим ( или даже не существует, например TCP-over-carrier pigeon). Сериализуемые объекты на других r рука всегда имеет такое же представление, например файл PNG, который я могу прочитать на рабочем столе Windows, на телефоне или на зубной щетке. Переносимые вещи - это внутренние, сериализуемые вещи имеют дело с вводом-выводом. Переносимые вещи безопасны по типу, сериализуемые вещи нуждаются в типизации.

Когда дело доходит до обработки символов в C, есть две группы вещей, связанных соответственно с переносимостью и сериализацией:

  • wchar_t , setlocale () , mbsrtowcs () / wcsrtombs () : Стандарт C ничего не говорит о "кодировках" ; фактически, он полностью не зависит от свойств текста или кодировки. Он только говорит: «ваша точка входа - main (int, char **) ; вы получаете тип wchar_t , который может содержать все символы вашей системы; вы получаете функции для чтения входных char- последовательности и превращать их в рабочие строки wstrings и наоборот.

  • iconv () и UTF-8,16,32: функция / библиотека для перекодирования между четко определенными, определенными, фиксированными кодировками. Все кодировки обрабатываются iconv универсально понятны и согласованы, за одним исключением.

Мостом между переносимым, не зависящим от кодирования миром C с его wchar_t переносимым типом символов и детерминированным внешним миром является преобразование iconv между WCHAR-T и UTF .

Итак, я должен всегда хранить свои строки внутри в независимой от кодировки wstring, взаимодействовать с CRT через wcsrtombs () и использовать iconv ( ) для сериализации? Концептуально:

                        my program
    <-- wcstombs ---  /==============\   --- iconv(UTF8, WCHAR_T) -->
CRT                   |   wchar_t[]  |                                <Disk>
    --- mbstowcs -->  \==============/   <-- iconv(WCHAR_T, UTF8) ---
                            |
                            +-- iconv(WCHAR_T, UCS-4) --+
                                                        |
       ... <--- (adv. Unicode malarkey) ----- libicu ---+

Практически это означает, что я бы написал две стандартные оболочки для m y точка входа в программу, например для C ++:

// Portable wmain()-wrapper
#include <clocale>
#include <cwchar>
#include <string>
#include <vector>

std::vector<std::wstring> parse(int argc, char * argv[]); // use mbsrtowcs etc

int wmain(const std::vector<std::wstring> args); // user starts here

#if defined(_WIN32) || defined(WIN32)
#include <windows.h>
extern "C" int main()
{
  setlocale(LC_CTYPE, "");
  int argc;
  wchar_t * const * const argv = CommandLineToArgvW(GetCommandLineW(), &argc);
  return wmain(std::vector<std::wstring>(argv, argv + argc));
}
#else
extern "C" int main(int argc, char * argv[])
{
  setlocale(LC_CTYPE, "");
  return wmain(parse(argc, argv));
}
#endif
// Serialization utilities

#include <iconv.h>

typedef std::basic_string<uint16_t> U16String;
typedef std::basic_string<uint32_t> U32String;

U16String toUTF16(std::wstring s);
U32String toUTF32(std::wstring s);

/* ... */

Правильный ли это способ написать идиоматическое, переносимое, универсальное, независимое от кодирования ядро ​​программы, используя только чистый стандартный C / C ++, вместе с четко определенным интерфейсом ввода-вывода для UTF с использованием iconv? (Обратите внимание, что такие вопросы, как нормализация Unicode или замена диакритических знаков, выходят за рамки; только после того, как вы решите, что вам действительно нужен Unicode (в отличие от любой другой системы кодирования, которую вы можете себе представить), пора разобраться с этими особенностями , например, используя специальную библиотеку, такую ​​как libicu.)

Обновления

После многих очень хороших комментариев я хотел бы добавить несколько наблюдений:

  • Если ваше приложение явно хочет иметь дело с текстом Unicode, вы должны сделать iconv -преобразование части ядра и использование uint32_t / char32_t -строки внутри UCS-4.

  • Windows: использование широких строк в целом нормально , похоже, что взаимодействие с консолью (с любой консолью, если на то пошло) ограничено, поскольку, похоже, нет поддержки какой-либо разумной многобайтовой кодировки консоли, а mbstowcs по существу бесполезен (кроме как для тривиальное расширение). Получение аргументов с широкими строками, скажем, от Explorer-drop вместе с GetCommandLineW + CommandLineToArgvW работает (возможно, должна быть отдельная оболочка для Windows).

  • Файловые системы: Файловые системы, похоже, не имеют никакого понятия о кодировке и просто принимают любую строку с завершающим нулем в качестве имени файла. Большинство систем принимают байтовые строки, но Windows / NTFS принимает 16-битные строки. Вы должны проявлять осторожность при обнаружении существующих файлов и при обработке этих данных (например, последовательности char16_t , которые не составляют допустимый UTF16 (например, голые суррогаты), являются допустимыми именами файлов NTFS). Стандартный C fopen не может открывать все файлы NTFS, так как не существует возможного преобразования, которое отображало бы все возможные 16-битные строки. Может потребоваться использование специфичного для Windows _wfopen . Как следствие, в целом не существует четко определенного понятия «сколько символов» содержится в данном имени файла, так как в первую очередь отсутствует понятие «символ». Caveat emptor.

60
задан Kerrek SB 14 June 2011 в 13:03
поделиться