Поскольку меня попросили, я сделаю некромантию. Другие ответы были с 2009 года, но эта статья все еще была найдена в поиске, который я сделал в 2018 году. Ситуация сегодня совсем иная. Кроме того, принятый ответ был неполным даже в 2009 году.
Исходный набор символов
Каждый компилятор (включая Microsoft Visual Studio 2008 и более поздние версии, gcc, clang и icc) будет читать исходные файлы UTF-8, которые начинаются с спецификации, без проблем, а clang не будет читать ничего, кроме UTF-8, поэтому UTF-8 с спецификацией - самый низкий общий знаменатель для исходных файлов C и C ++.
В стандарте языка не указано, какие исходные наборы символов должен поддерживать компилятор. Некоторые реальные исходные файлы даже сохраняются в наборе символов, несовместимом с ASCII. Microsoft Visual C ++ в 2008 году поддерживал исходные файлы UTF-8 с меткой порядка байтов, а также обе формы UTF-16. Без метки порядка байтов предполагается, что файл был закодирован в текущей 8-битной кодовой странице, которая всегда была надмножеством ASCII.
Наборы символов исполнения
В 2012 году компилятор добавил переключатель /utf-8
в CL.EXE
. Сегодня он также поддерживает переключатели /source-charset
и /execution-charset
, а также /validate-charset
, чтобы определить, не является ли ваш файл на самом деле UTF-8. На этой странице в MSDN есть ссылка на документацию по поддержке Unicode для каждой версии Visual C ++.
Текущие версии стандарта C ++ говорят, что компилятор должен иметь как набор символов выполнения, который определяет числовое значение символьных констант, таких как 'a'
, так и набор широких символов выполнения, который определяет значение констант широких символов, таких как L'é'
.
Немного языковой юрист, в стандарте очень мало требований к тому, как они должны быть закодированы, и все же Visual C и C ++ могут их нарушить. Он должен содержать около 100 символов, которые не могут иметь отрицательных значений, а кодировка цифр от '0'
до '9'
должна быть последовательной. Ни заглавные, ни строчные буквы не должны быть, потому что они не были на некоторых старых мэйнфреймах. (То есть '0'+9
должно быть таким же, как '9'
, но сегодня в реальном мире все еще существует компилятор, поведение по умолчанию которого состоит в том, что 'a'+9
не 'j'
, а '«'
, и это допустимо .) Широкий набор символов должен включать базовый набор и иметь достаточно битов для хранения всех символов любой поддерживаемой локали. Каждый основной компилятор поддерживает как минимум одну локаль Unicode и понимает допустимые символы Unicode, указанные в \Uxxxxxxxx
, но компилятор, который не может претендовать на соответствие стандарту.
То, как Visual C и C ++ нарушают языковой стандарт, заключается в создании их wchar_t
UTF-16, который может представлять только некоторые символы в качестве суррогатных пар, когда стандарт говорит, что wchar_t
должна быть кодировкой фиксированной ширины. Это связано с тем, что Microsoft определила wchar_t
как 16-битную ширину еще в 1990-х годах, прежде чем комитет по Юникоду выяснил, что 16-битных будет недостаточно для всего мира, и Microsoft не собирается нарушать Windows API. Он также поддерживает стандартный тип char32_t
.
Строковые литералы UTF-8
Третья проблема, которую поднимает этот вопрос, заключается в том, как заставить компилятор кодировать строковый литерал как UTF-8 в памяти. Вы можете написать что-то подобное с C ++ 11:
constexpr unsigned char hola_utf8[] = u8"¡Hola, mundo!";
Это закодирует строку в виде представления байтов UTF-8 с нулевым символом в конце независимо от того, является ли исходный набор символов UTF-8, UTF-16, Latin-1, CP1252 или даже IBM EBCDIC 1047 (что глупо теоретический пример, но для обратной совместимости по умолчанию используется компилятор мэйнфреймов IBM серии Z по умолчанию). То есть это эквивалентно инициализации массива с { 0xC2, 0xA1, 'H', /* ... , */ '!', 0 }
.
Если это будетБыло бы слишком неудобно вводить символ или если вы хотите различать внешне идентичные символы, такие как пробел и неразрывный пробел или предварительно составленные и комбинируемые символы, у вас также есть универсальные экранированные символы:
constexpr unsigned char hola_utf8[] = u8"\u00a1Hola, mundo!";
Вы можете использовать их независимо от исходного набора символов и независимо от того, сохраняете ли вы литерал как UTF-8, UTF-16 или UCS-4. Первоначально они были добавлены в C99, но Microsoft поддержала их в Visual Studio 2015. Однако существует другой способ сделать это, который работал в Visual C или C ++ 2008: восьмеричные и шестнадцатеричные коды перехода. Вы бы закодировали литералы UTF-8 в этой версии компилятора с помощью:
const unsigned char hola_utf8[] = "\xC2\xA1Hello, world!";