Пожалуйста, попробуйте выполнить следующие действия:
******* Наконец, это сделано ******
Итак, ваша проблема связана с смешиванием вызовов библиотеки C старой школы (getc, printf% c) и UTF-8. Ваш код правильно считывает три байта, которые составляют «€» - 226, 130 и 172 как десятичные, но эти значения по отдельности не являются действительными глифами в кодировке UTF-8.
Если вы посмотрите на кодировку UTF-8 , то целочисленные значения 0..127 являются кодировками для исходного набора символов US-ASCII. Однако 128..255 (т.е. все ваши байты) являются частью многобайтового символа UTF-8 и поэтому не соответствуют действительному символу UTF-8 постоянно.
Другими словами, однобайтный '226' не соответствует ' t означает что-либо само по себе (поскольку это префикс для 3-байтового символа - как и ожидалось). Вызов printf
печатает его как один байт, что недопустимо для кодировки UTF-8,
Первое, что вы написали:
Character: � Integer: 226
Character: �, Integer: 130
Character: �, Integer: 172
Это «правильный» ответ. Когда вы печатаете символ 226, и терминал ожидает utf8, терминал ничего не может сделать, вы дали ему недопустимые данные. Последовательность «226» «пробел» является ошибкой. ? Символ - хороший способ показать вам, что где-то есть искаженные данные.
Если вы хотите воспроизвести второй пример, вам нужно правильно закодировать символ.
Представьте себе две функции; decode, который принимает кодировку символов и поток октетов и создает список символов; и encode, который принимает кодировку списка символов и создает поток октетов. кодирование / декодирование должно быть обратимым, если ваши данные действительны: encode ('utf8', decode ('utf8', «...»)) == «...».
В любом случае, во втором примере приложение («коврик для мыши?») обрабатывает каждый октет в трехоктетном представлении символа евро как отдельный символ latin1. Он получает октет, декодирует его от latin-1 до некоторого внутреннего представления «символа» (не октета или байта), а затем кодирует этот символ как utf8 и записывает его в терминал. Вот почему это работает.
Если у вас есть GNU Recode, попробуйте следующее:
$ recode latin1..utf8
<three-octet representation of the euro character> <control-D>
â¬
Это обработало каждый октет представления utf-8 как символ latin1, а затем преобразовало каждый из этих символов в то, что ваш терминал может Понимаю. Возможно, прохождение этого через hd проясняет:
$ cat | hd
€
00000000 e2 82 ac 0a |....|
00000004
Как видите, это 3 октета для представления символа в формате utf-8, а затем перевод строки.
Выполнение перекодировки:
$ recode latin1..utf8 | hd
€
00000000 c3 a2 c2 82 c2 ac 0a |.......|
00000007
Это utf- 8 представление входной строки "latin1"; то, что может отображать ваш терминал. Идея в том, что если вы выведете на свой терминал, вы увидите знак евро. Если вы выведете, вы ничего не получите, это неверно. Наконец, если вы выведете, вы получите «мусор», который является «представлением символа в формате utf-8».
Если это кажется запутанным, это так. Вы никогда не должны беспокоиться о таком внутреннем представлении; если вы работаете с символами и вам нужно распечатать их на терминале utf-8, вы всегда должны кодировать в utf-8. Если вы читаете файл в кодировке utf-8, вам необходимо декодировать октеты в символы перед их обработкой в приложении.
Кодировка UTF-8 говорит, что три байта вместе в строке образуют знак евро или '€'. Но одиночные байты, подобные тем, которые создаются вашей программой на C, не имеют смысла в потоке UTF-8. Вот почему они заменены U + FFFD «ЗАМЕНА ЗНАЧЕНИЯ» или « ».
E-macs умен, он знает, что отдельные байты являются недопустимыми данными для выходного потока, и заменяет их видимым escape-представлением байта. Вывод коврика для мыши действительно не работает, я не понимаю. Коврик для мыши возвращается к кодовой странице CP1252 Windows, где отдельные байты представляют символы. «Запятая» - это не запятая, это низкая изогнутая цитата .