2016-09-25 3 views
3

Предположим, что имеется только C99 Standard бумага и printf Функция библиотеки должна быть реализована в соответствии с этим стандартом для работы с кодировкой UTF-16, не могли бы вы прояснить ожидаемое поведение для преобразования s с точностью указано?C99 Стандарт - fprintf - s преобразование с точностью

С99 Стандартный (7.19.6.1) для преобразования s говорит:

Если ни один модификатор л Длина не присутствует, аргумент должен быть указателем на начальный элемент массива символьного типа. Символы из массива записываются в (но не включая) завершающий нулевой символ. Если задана точность, то не больше, чем написано много байтов. Если точность не указана или больше размера массива, массив должен содержать нулевой символ.

Если присутствует модификатор длины l, аргумент должен быть указателем на исходный элемент массива типа wchar_t. Широкие символы из массива преобразуются в многобайтовые символы (каждый, как если бы вызывал функцию wcrtomb, с состоянием преобразования, описанным объектом mbstate_t, инициализированным до нуля до первого первого символа), вплоть до нулевой ширины персонаж. Полученные многобайтовые символы записываются в (но не включая) завершающий нулевой символ (байт). Если точность не указана, массив должен содержать нулевой широкий символ. Если задана точность, то не больше, чем записано много байтов (включая последовательности сдвигов, если они есть), и массив должен содержать нулевой широкий символ, если для того, чтобы равняться длине последовательности многобайтовых символов, заданной точностью, функция должна была бы для доступа к широкому символу за концом массива. Ни в коем случае не является частичным многобайтовым символом.

Я не совсем понимаю этот параграф в целом и утверждение «Если указана точность, в частности, не больше, чем указано много байтов».

Например, возьмем строку UTF-16 «TEST» (последовательность байтов: 0x54, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00).

Что, как ожидается, будут записаны в выходной буфер в следующих случаях:

  • Если точность составляет 3
  • Если точность 9 (один байт больше, чем длина строки)
  • Если точность составляет 12 (несколько байт больше длины шнура)

Тогда есть также «Широкие символы из массива преобразуются в многобайтовые символы». Означает ли это, что UTF-16 следует сначала преобразовать в UTF-8? Это довольно странно, если я буду работать только с UTF-16.

+0

'% s' принимает строку. Вы не можете хранить UTF-16 в строке C из-за всех нулевых байтов. (Кроме того, UTF-16 не является набором символов, это кодировка.) – melpomene

+0

'' \ x54 \ x00 \ x45 \ x00 \ x53 \ x00 \ x54 \ x00 "' представляет собой строку длиной 1, содержащую 'T'. – melpomene

+0

Вы конкретно спрашиваете о '% ls' /' wchar_t'? – melpomene

ответ

1

Преобразование комментария в слегка расширенный ответ.

Какая ценность CHAR_BIT в вашей реализации?

  • Если CHAR_BIT == 8, вы не можете справиться с UTF-16 с %s; вы должны использовать %ls, и вы должны передать wchar_t * в качестве соответствующего аргумента. Затем вам нужно будет прочитать второй абзац спецификации.

  • Если CHAR_BIT == 16, то у вас не может быть нечетного количества октетов в данных. Затем вам нужно знать о том, как wchar_t относится к char (они одинакового размера? Имеют ли они одинаковую подпись?) И интерпретируют оба абзаца, чтобы создать единый эффект - если вы не решили, что wchar_t представляют UTF-32.

Ключевым моментом является то, что UTF-16 не может быть обработан в виде строки C, если CHAR_BIT == 8, потому что есть слишком много полезных символов, которые кодируются одним байтом держит ноль, но эти нулевые байты отмечают конец нуль -терминированная строка. Для обработки UTF-16 либо простой тип char должен быть 16-разрядным (или более крупным) типом (так CHAR_BIT > 8), либо вам необходимо использовать (и sizeof(wchar_t) > sizeof(char)).

Обратите внимание, что в спецификации ожидается, что широкие символы будут преобразованы в подходящее многобайтовое представление.

Если вы хотите, чтобы широкие символы выводились изначально, вы должны использовать fwprintf() и связанную функцию от <wchar.h>, сначала определяемую на C99. Спецификация там имеет много общего со спецификацией fprintf(), но есть (неудивительно) важные отличия.

7.29.2.1 Функция fwprintf

...

s
Если ни один модификатор l длина не присутствует, аргумент должен быть указателем на начальный элемента массива символов, содержащего многобайтовая последовательность символов , начинающаяся в исходном состоянии сдвига. Символы из массива преобразуются в , если при повторных вызовах функции mbrtowc, с состоянием преобразования , описываемое mbstate_t объекта инициализируется в нуль перед первым многобайтовым символом преобразуются и записываются до (но не включая) завершение null широкий знак. Если задана точность, не более , что написано много широких символов. Если точность не указана или больше размера преобразованного массива, преобразованный массив должен содержать нулевой символ .

Если модификатор l длины присутствует, аргумент должен быть указателем на начальный элемент массива wchar_t типа. Широкие символы из массива - это , записанные до (но не включая) завершающий нулевой символ. Если указана точность , пишется не более того, что написано много широких символов. Если точность не указана или больше размера массива, массив должен содержать нулевой ширину.

1

wchar_t не предназначена для использования в UTF-16, только для выполнения определенных фиксированной ширины кодирования в зависимости от текущей локали. Просто нет разумного способа поддержки кодирования переменной длины с широким API-интерфейсом. Аналогично, многобайтовое представление, используемое такими функциями, как printf или wcrtomb, является реализацией. Если вы хотите написать переносимый код с помощью Unicode, вы не можете полагаться на широкоформатный API. Используйте библиотеку или сворачивайте свой собственный код.

Чтобы ответить на ваш вопрос: fprintf с модификатором l принимает широкую строку символов в кодировке, определенной в соответствии с текущей локалью. Если wchar_t - это 16 бит, эта кодировка может быть бастардизацией UTF-16, но, как я уже упоминал выше, нет возможности правильно поддерживать суррогаты UTF-16. Строка wchar_t затем преобразуется в многобайтную строку char в кодировке, определяемой реализацией. Это может быть или не быть UTF-8. Указанная точность ограничивает число char s в выходной строке с добавленным ограничением на то, что не записаны частичные многобайтные символы.

Вот пример. Предположим, что широкая кодировка символов - это UTF-32 с 32-битным wchar_t и что многобайтовая кодировка - UTF-8 (например, Linux с appropriate locale). Следующий код

wchar_t w[] = { 0x1F600, 0 }; // U+1F600 GRINNING FACE 
printf("%.3ls", w); 

ничего не печатает, так как полученная последовательность UTF-8 имеет четыре байта. Только если вы указали точность не менее четырех

printf("%.4ls", w); 

персонаж будет напечатан.

EDIT: Чтобы ответить на ваш второй вопрос, нет, printf никогда не должен писать нулевой символ. Предложение только означает, что в некоторых случаях требуется указать нулевой символ, чтобы указать конец строки и избежать чрезмерного чтения буфера.

+0

@AlexanderZhak См. Отредактированный вопрос. – nwellnhof

Смежные вопросы