Emacs, xterm, коврик для мыши, C, Unicode и UTF-8: пытаясь понять все это - PullRequest
5 голосов
/ 18 июля 2009

Отказ от ответственности : Мои извинения за весь текст ниже (за один простой вопрос), но я искренне считаю, что каждый бит информации имеет отношение к вопросу. Я был бы счастлив узнать иначе. Я могу только надеяться, что, в случае успеха, вопрос (ы) и ответы могут помочь другим в безумии Unicode. Здесь идет.

Я прочитал все обычно высоко ценимые веб-сайты о utf8, в частности этот очень хорош для моих целей, но я также читал классику, как те, которые упоминались в других подобных вопросах в SO , Однако мне все еще не хватает знаний о том, как интегрировать все это в мою виртуальную лабораторию. Я использую Emacs с

;; Internationalization
(prefer-coding-system 'utf-8)
(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)

в моем .emacs, xterm начинался с

 LC_CTYPE=en_US.UTF-8 xterm -geometry 91x58\
-fn '-Misc-Fixed-Medium-R-SemiCondensed--13-120-75-75-C-60-ISO10646-1'

и мой язык звучит так:

LANG=en_US.UTF-8
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

Мои вопросы следующие (некоторые ответы могут быть ожидаемым поведением приложения, но мне все еще нужно разобраться в этом, так что терпите меня):

Предположим, следующая программа на C:

#include <stdio.h>

int main(void) {
  int c;
  while((c=getc(stdin))!=EOF) {
    if(c!='\n') {
      printf("Character: %c, Integer: %d\n", c, c);
    }
  }
  return 0;
}

Если я запускаю это в моем xterm, я получаю:

€
Character: � Integer: 226
Character: �, Integer: 130
Character: �, Integer: 172

(на всякий случай, если я получу символы, это белый знак вопроса внутри черного круга). Интенты - это десятичное представление 3 байтов, необходимых для кодирования €, но я не совсем уверен, почему xterm не отображает их должным образом.

Вместо этого коврик для мыши, например, печатает

Character: â, Integer: 226
Character: ,, Integer: 130 (a comma, standing forU+0082 <control>, why?!)
Character: ¬, Integer: 172

Между тем, Emacs отображает

Character: \342, Integer: 226
Character: \202, Integer: 130
Character: \254, Integer: 172

ВОПРОС : Самый общий вопрос, который я могу задать: как мне заставить все печатать один и тот же символ? Но я уверен, что будут последующие меры.

Еще раз спасибо и извинения за весь текст.

Ответы [ 3 ]

7 голосов
/ 18 июля 2009

Итак, ваша проблема здесь в том, что вы смешали вызовы старой библиотеки C (getc, printf% c) и UTF-8. Ваш код правильно читает три байта, которые составляют '€' - 226, 130 и 172 как десятичные числа, но эти значения по отдельности не являются действительными глифами в кодировке UTF-8.

Если вы посмотрите на кодировку UTF-8 , целочисленные значения 0..127 - это кодировки для исходного набора символов US-ASCII. Однако 128..255 (то есть все ваши байты) являются частью многобайтового символа UTF-8 и поэтому не соответствуют действительному символу UTF-8 по отдельности.

Другими словами, одиночный байт '226' сам по себе ничего не значит (так как это префикс для 3-байтового символа - как и ожидалось). Вызов printf печатает его в виде одного байта, что недопустимо в кодировке UTF-8, поэтому каждая отдельная программа по-разному справляется с недопустимым значением.

Предполагая, что вы просто хотите «увидеть», из чего состоят байты символа UTF-8, я предлагаю вам придерживаться целочисленного вывода, который у вас уже есть (или, возможно, использовать шестнадцатеричный, если это более разумно) - в качестве> 127 байт неверный юникод, вы вряд ли получите согласованные результаты в разных программах.

3 голосов
/ 18 июля 2009

Кодировка UTF-8 говорит, что три байта вместе в строке образуют знак евро, или '€'. Но отдельные байты, подобные тем, которые создаются вашей C-программой, не имеют смысла в потоке UTF-8. Вот почему они заменяются на U + FFFD «ЗАМЕНЯЮЩИЙ ХАРАКТЕР» или « ».

E-macs умный, он знает, что отдельные байты являются недопустимыми данными для выходного потока, и заменяет его видимым экранирующим представлением байта. Вывод коврика для мыши действительно поврежден, я не могу ничего понять. Коврик для мыши возвращается к кодовой странице Windows CP1252, где отдельные байты представляют символы. "Запятая" - это не запятая, это низкая изогнутая цитата .

1 голос
/ 18 июля 2009

Первое, что вы опубликовали:

Character: � Integer: 226
Character: �, Integer: 130
Character: �, Integer: 172

Является ли «правильный» ответ. Когда вы печатаете символ 226 и терминал ожидает utf8, терминал ничего не может сделать, вы передали ему недопустимые данные. Последовательность «226» «пробел» является ошибкой. ? персонаж - хороший способ показать вам, что где-то есть искаженные данные.

Если вы хотите повторить ваш второй пример, вам нужно правильно закодировать символ.

Вообразите две функции; декодирование, которое принимает кодировку символов и поток октетов и создает список символов; и закодировать, который принимает кодирование списка символов и создает поток октетов. Кодирование / декодирование должно быть обратимым, когда ваши данные действительны: encode ('utf8', decode ('utf8', "...")) == "...".

Так или иначе, во втором примере приложение («коврик для мыши?») Обрабатывает каждый октет в трех октетном представлении символа евро как отдельный символ латиницы 1. Он получает октет, декодирует его из латиницы-1 во некоторое внутреннее представление «символа» (не октета или байта), а затем кодирует этот символ как utf8 и записывает его в терминал. Вот почему это работает.

Если у вас есть GNU Recode, попробуйте это:

$ recode latin1..utf8
<three-octet representation of the euro character> <control-D>
â¬

То, что это делало, обрабатывало каждый октет представления utf-8 как символ латиницы 1, а затем преобразовывал каждый из этих символов в то, что ваш терминал может понять. Возможно, запуск этого через 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, вам необходимо декодировать октеты в символы перед их обработкой в ​​приложении.

...