Получение неправильных значений UTF-8 путем приведения char в USHORT - PullRequest
0 голосов
/ 25 апреля 2018

Это мой первый вопрос, поэтому не стесняйтесь критиковать или исправлять меня, если я упускаю важные правила.

Недавно мне было поручено перенести старый код DOS C на платформу Linux.Обработка шрифтов осуществляется с помощью bitfonts.Я написал функцию, которая способна рисовать выбранный глиф, если вы передадите в него правильное значение Юникода.

Однако, если я попытаюсь привести символ в USHORT (функции ожидают этого типа), я получу неправильныйзначение, когда символ находится за пределами ASCII-таблицы.

char* test;
test = "°";

printf("test: %hu\n",(USHORT)test[0]);

Отображаемое число (консоль) должно быть 176, но вместо этого 194.

Если вы используете "!"правильное значение 33 будет отображаться.Я удостоверился, что char без знака, установив флаг компилятора GCC

-unsigned-char

Компилятор GCC использует кодировку UTF-8 по умолчанию.Я действительно не знаю, где проблема сейчас.

Нужно ли добавить еще один флаг в компилятор?

Обновление

СС помощью ответа @Kninnug мне удалось написать код, который даст желаемые результаты для меня.

#include <stdio.h>
#include <locale.h>
#include <string.h>
#include <wchar.h>
#include <stdint.h>

int main(void)
{
   size_t n = 0, x = 0;
   setlocale(LC_CTYPE, "en_US.utf8");
   mbstate_t state = {0};
   char in[] = "!°水"; // or u8"zß水"
   size_t in_sz = sizeof(in) / sizeof (*in);

   printf("Processing %zu UTF-8 code units: [ ", in_sz);
   for(n = 0; n < in_sz; ++n)
   {
      printf("%#x ", (unsigned char)in[n]);
   }
   puts("]");

   wchar_t out[in_sz];
   char* p_in = in, *end = in + in_sz;
   wchar_t *p_out = out;
   int rc = 0;
   while((rc = mbrtowc(p_out, p_in, end - p_in, &state)) > 0)
   {
       p_in += rc;
       p_out += 1;
   }

   size_t out_sz = p_out - out + 1;
   printf("into %zu wchar_t units: [ ", out_sz);
   for(x = 0; x < out_sz; ++x)
   {
      printf("%u ", (unsigned short)out[x]);
   }
   puts("]");
}

Однако, когда я запускаю это на своем встроенном устройстве, символы не-ASCII объединяются водин wchar, а не на два, как на моем компьютере.

Я мог бы использовать однобайтовое кодирование с cp1252 (это работало нормально), но я бы хотел продолжать использовать юникод.

1 Ответ

0 голосов
/ 25 апреля 2018

A char (signed или unsigned) - это один байт в C 1 .(USHORT)test[0] только бросает только первый байт в test, но символ в нем занимает 2 в кодировке UTF-8 (вы можете проверить это с помощью strlen, который считает количество байтов перед первым 0-байтом).

Чтобы получить правильную кодовую точку, вам необходимо декодировать всю последовательность UTF-8.Вы можете сделать это с помощью mbrtowc и связанных функций:

char* test;
test = "°";
int len = strlen(test);

wchar_t code = 0;
mbstate_t state = {0};

// convert up to len bytes in test, and put the result in code
// state is used when there are incomplete sequences: pass it to
// the next call to continue decoding
mbrtowc(&code, test, len, &state); // you should check the return value

// here the cast is needed, since a wchar_t is not (necessarily) a short
printf("test: %hu\n", (USHORT)code); 

Примечания:

  • Если USHORT равно 16 битам (как это обычно бывает), недостаточно строго охватить весь диапазон UTF-8, для которого требуется (как минимум) 21 бит.

  • Когда вы получили правильную кодовую точку, приведение не должно быть необходимым для передачи его функции рисования.Если определение функции или прототип видимы, компилятор может преобразовать значение самостоятельно.

1 Непонятное название происходит от того времени, когда все английские всего мира и все кодовые точки ASCII могут помещаться в один байт.Следовательно, символ был таким же, как байт.

...