Ответ 1
Префикс u8
действительно просто означает "при компиляции этого кода, сгенерируйте строку UTF-8 из этого литерала". В нем ничего не говорится о том, как литерал в исходном файле должен интерпретироваться компилятором.
Итак, у вас есть несколько факторов:
- какая кодировка является исходным файлом, записанным в (в вашем случае, по-видимому, ISO-8859). Согласно этой кодировке строковый литерал "åäö" (3 байта, содержащий значения 0xc5, 0xe4, 0xf6)
- какая кодировка делает компилятор при чтении исходного файла? (Я подозреваю, что GCC по умолчанию использует UTF-8, но я могу ошибаться.
- кодировка, которую компилятор использует для сгенерированной строки в объектном файле. Вы указываете, что это UTF-8 через префикс
u8
.
Скорее всего, №2 - это то, где это происходит неправильно. Если компилятор интерпретирует исходный файл как ISO-8859, он считывает три символа, преобразует их в UTF-8 и записывает их, предоставляя вам 6-байтовое (я думаю, каждый из этих символов кодирует до 2 байтов в UTF -8) в результате.
Однако, если он предполагает, что исходным файлом является UTF-8, тогда вообще не нужно делать преобразование: он считывает 3 байта, который он предполагает, это UTF-8 (хотя они являются недопустимыми мусорами значения для UTF-8), и поскольку вы запросили также, чтобы выходная строка была UTF-8, она просто выводит те же 3 байта.
Вы можете указать GCC, какую исходную кодировку предполагается использовать с помощью -finput-charset
, или вы можете закодировать источник как UTF-8, или вы можете использовать escape-последовательности \uXXXX
в строковом литерале (\u00E5
вместо å
, например)
Изменить:
Чтобы уточнить бит, когда вы указываете строковый литерал с префиксом u8
в исходном коде, вы сообщаете компилятору, что "независимо от того, какую кодировку вы использовали при чтении исходного текста, пожалуйста, преобразуйте его в UTF -8 при записи его в файл объекта". Вы ничего не говорите о том, как интерпретировать исходный текст. Это зависит от того, какой компилятор должен решить (возможно, на основе того, какие флаги вы передали ему, возможно, на основе среды процесса или, возможно, просто с использованием жесткого кодированного значения по умолчанию)
Если строка в исходном тексте содержит байты 0xc5, 0xe4, 0xf6, и вы скажете, что "исходный текст закодирован как ISO-8859", тогда компилятор распознает, что "строка состоит из символов", åäö ". Он увидит префикс u8
и преобразует эти символы в UTF-8, записав байтовую последовательность 0xc3, 0xa5, 0xc3, 0xa4, 0xc3, 0xb6 в объектный файл. В этом случае вы получите действительная кодированная текстовая строка UTF-8, содержащая представление UTF-8 символов "åäö".
Однако, если строка в исходном тексте содержит один и тот же байт, и вы делаете компилятор уверенным, что исходный текст закодирован как UTF-8, тогда есть две вещи, которые может сделать компилятор (в зависимости от реализации:
- он может попытаться проанализировать байты как UTF-8, и в этом случае он распознает, что "это не допустимая последовательность UTF-8" и выдает ошибку. Это то, что делает Кланг.
- в качестве альтернативы, он может сказать: "Хорошо, у меня здесь 3 байта, мне сказали предположить, что они образуют правильную строку UTF-8. Я буду следить за ними и посмотреть, что произойдет". Затем, когда предполагается записать строку в объектный файл, она идет "ok", у меня есть эти 3 байта от ранее, которые обозначены как UTF-8. Префикс
u8
здесь означает, что я должен писать эта строка как UTF-8. Прохладный, не нужно делать преобразование, тогда я просто напишу эти 3 байта, и я закончил ". Это то, что делает GCC.
Оба действительны. Язык С++ не указывает, что компилятор должен проверить правильность строковых литералов, которые вы передаете ему.
Но в обоих случаях обратите внимание, что префикс u8
не имеет ничего общего с вашей проблемой. Это просто говорит компилятору преобразовать из "любой кодировки, которую имела строка при ее чтении, в UTF-8". Но даже до этого преобразования строка была уже искажена, потому что байты соответствовали символьным данным ISO-8859, но компилятор считал их UTF-8 (потому что вы не говорили об этом иначе).
Проблема, которую вы видите, просто заключается в том, что компилятор не знал, какую кодировку использовать при чтении строкового литерала из вашего исходного файла.
Другая вещь, которую вы замечаете, заключается в том, что "традиционный" строковый литерал без префикса будет закодирован с любой кодировкой, которую любит компилятор. Префикс u8
(и соответствующие префиксы UTF-16 и UTF-32) были введены точно, чтобы вы могли указать, какую кодировку вы хотите, чтобы компилятор записывал вывод. В простых литералах без префикса не указывается кодировка в все, оставляя это до компилятора, чтобы решить одно.