Поведение расширенных байтов/символов в локали C/POSIX
C и POSIX требуют наличия только очень ограниченного набора символов в локали C/POSIX, но допускают существование дополнительных символов. Это оставляет большую свободу для реализации; например, поддержка всех Unicode (как UTF-8) в локали C соответствует поведению. Однако большинство исторических реализаций относятся к языку C как к 8-битовому чистому однобайтовому кодированию символов: ISO-8859-1 (Latin-1) или своего рода "абстрактному 8-битовому набору символов", где байты без ASCII являются абстрактными символами без определенного идентификатора. (Однако в последнем случае, если компилятор определяет __STDC_ISO_10646__
, они нормативно соответствуют символам Unicode, обычно диапазону Latin-1.)
Другим подходящим вариантом, который кажется гораздо менее популярным, является обработка всех байтов без ASCII как несимвольных, то есть ответ на них с ошибкой EILSEQ
.
Мне интересно знать, существуют ли реализации, которые используют этот или любые другие необычные варианты реализации языковой версии C. Существуют ли реализации, в которых попытка конвертировать "высокие байты" в локаль C приводит к EILSEQ
или к чему-то другому, кроме как обрабатывать их как (абстрактные или латинские-1) однобайтовые символы или UTF-8?
Ответы
Ответ 1
От комментария к предыдущему ответу:
Способы ошибочного предположения в основном состоят в том, что байты вне переносимого набора символов могут быть незаконными несимвольными байтами (EILSEQ) или составлять некоторую многобайтовую кодировку (UTF-8 или устаревшее кодирование CJK)
Здесь вы можете найти один пример.
План 9 поддерживает только локаль C. Как вы можете видеть в utf.c и rune.c, когда он находит руну за пределами переносимых символов, она просто обрабатывает ее как символ из другой кодировки.
Другими кандидатами могут быть Minix, а * Семейство BSD (поскольку они используют citrus). В исходном коде Minix я также нашел команду file, которая ищет новую кодировку, когда размер символа не равен 8 бит.
Ответ 2
"Мне интересно знать, есть ли реализации, которые берут этот или любые другие необычные варианты при реализации языкового стандарта C".
Этот вопрос очень сложно ответить, поскольку он смешивает "C Locale", который я предполагаю, относится к ограниченному набору символов C Standard, упомянутому выше, с "другими необычными вариантами", который я предполагаю, относится к тому, как конкретная реализация обрабатывает символы вне (ограниченный) язык C. Каждая реализация C должна внедрять C Locale; Я не думаю, что там есть какие-то необычные варианты.
Предположим для аргумента, что вопрос заключается в следующем: "... необычные варианты при реализации дополнительных/расширенных символов за пределами языкового стандарта C". Теперь это становится вопросом, зависящим от реализации, и, как вы уже упоминали, он "оставляет большую свободу для реализации". Поэтому, не зная целевого компилятора/аппаратного обеспечения, все равно будет сложно ответить окончательно.
Теперь последняя часть:
"... попытка конвертировать" высокие байты "в локаль C приводит к EILSEQ или к чему-то другому, кроме как рассматривать их как (абстрактные или латинские-1) однобайтные символы или UTF-8?"
Вместо преобразования больших байтов в C Locale вы можете установить Locale в своей программе, как в этом SO-вопросе: Означает ли базовый набор символов только C:
Таким образом, вы можете гарантировать, что ваши персонажи будут обработаны в Locale, который вы ожидаете.
Я понимаю, что C Locale относится только к первым 7-битным (8-разрядным типом char
), основанным на следующих источниках:
Термины "высокие байты" и "Юникод" и "UTF-8" относятся к классу многобайтовых или широкосимвольных кодировок и являются специфическими для локали (и вне диапазона минимального C Locale). Я не понимаю, как можно "конвертировать большие байты" в (чистый) C Locale. Вполне возможно, что реализации выбрали бы стандартную (расширенную) локаль, если ни один из них не был явно установлен (или вытащил ее из настроек среды ОС, как указано в одной из вышеперечисленных ссылок).
Ответ 3
Поразительно, я просто обнаружил, что наиболее широко используемая реализация glibc - это пример того, что я ищу. Рассмотрим эту простую программу:
#include <stdlib.h>
#include <stdio.h>
int main()
{
wchar_t wc = 0;
int n = mbtowc(&wc, "\x80", 1);
printf("%d %.4x\n", n, (int)wc);
}
В glibc он печатает -1 0000
. Если байт 0x80 был расширенным символом в локали реализации C/POSIX, он будет печатать 1, за которым следует некоторый ненулевой номер символа.
Таким образом, "общее знание", что языковой стандарт C/POSIX "8-бит-чистый" в glibc, является просто ложным. Что происходит, так это то, что существует грубая несогласованность; несмотря на то, что все стандартные утилиты, согласование регулярных выражений и т.д. заданы для работы с (многобайтовыми) символами, как если бы они читались с помощью mbrtowc
, реализации этих утилит/функций принимают ярлык, когда они видят MB_CUR_MAX==1
или LC_CTYPE
содержащий "C"
(или аналогичный) и считывающий значения char
непосредственно вместо обработки ввода с помощью mbrtowc
или аналогичного. Это приводит к несогласованности между указанным поведением (которое, поскольку их реализация локали C/POSIX определено, должно обрабатывать большие байты как незаконные последовательности) и поведение реализации (которое полностью обходит систему локали).
Со всем сказанным я все еще ищу другие реализации со свойствами, заданными в вопросе.