Определить, является ли символ Unicode полной или половинной шириной в C++

Я пишу терминальное (консольное) приложение, которое должно обернуть произвольный текст в кодировке Unicode.

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

Проблема в том, что в таблице Unicode есть символы полной ширины, которые занимают ширину 2 символа в терминале.

Считая их, мы увидим 1 символ Юникода, но напечатанный символ имеет ширину в 2 "нормальных" (половинных) символа, что нарушает процедуру переноса, так как он не знает о символах, которые занимают вдвое большую ширину.

Например, это символ полной ширины (U+3004, символ JIS)

〄
12

Здесь он не занимает всю ширину в 2 символа, хотя он предварительно отформатирован, но он использует двойную ширину западного символа в терминале.

Чтобы справиться с этим, я должен различать символы полной и половинной ширины, но я не могу найти способ сделать это в C++. Действительно ли необходимо знать все символы полной ширины таблицы юникода, чтобы обойти проблему?

2 ответа

Решение

Вы должны использовать ICU u_getIntPropertyValue с UCHAR_EAST_ASIAN_WIDTH имущество.

Например:

bool is_fullwidth(UChar32 c) {
    int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
    return width == U_EA_FULLWIDTH || width == U_EA_WIDE;
}

Обратите внимание, что если ваша графическая библиотека поддерживает комбинирование символов, вам придется учитывать их и при определении количества ячеек, используемых последовательностью; например e с последующим U+0301 КОМБИНИРОВАНИЕ ОСТРОГО АКЦЕНТА займет всего 1 клетку.

Нет необходимости создавать таблицы, люди из Unicode уже сделали это:

http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c

Тот же код используется в программном обеспечении эмуляции терминала, таком как xterm [1], konsole [2] и, скорее всего, другие...

Другие вопросы по тегам