LINUX.ORG.RU

Написал статью «Как жить если у вас юникод»

 ,


5

4

Собственно, сабж. Статья про то самое, что мы с Eddy_Em не могли осилить в прежние времена. В этом году я это, внезапно, осилил. Ну и написал статью.

https://saahriktu.ru/pdf/kak_jit_esli_u_vas_yunikod.pdf

★★★★★
Ответ на: комментарий от anonymous2

аналог это std::u16string

Да. Но для char16_t раньше не хватало фасетов локализации, а без них потоки (std::basic_iostream) не работают (а используются они много где). И у майкрософта, и у libstdc++, и у libc++. Как сейчас не в курсе, может уже и доделали. Мне же при переделке виндового проекта на кроссплатформу пришлось при замене wchar_t на char16_t дописывать недостающие фасеты.

dvetutnev
()
Ответ на: комментарий от hateyoufeel

Букву Й, например, можно закодировать как кириллическую Й, или как кириллическую И плюс отдельную душку сверху, или ещё десятком разных способов. И во всех случаях при выводе на экран ты получишь Й

И нафига так сделали непонятно. Что мешало все эти вариации как отдельные символы добавить? Места же в таблице глифов хватает.

dvetutnev
()
Ответ на: комментарий от dvetutnev

Букву Й, например, можно закодировать как кириллическую Й, или как кириллическую И плюс отдельную душку сверху, или ещё десятком разных способов. И во всех случаях при выводе на экран ты получишь Й

И нафига так сделали непонятно. Что мешало все эти вариации как отдельные символы добавить? Места же в таблице глифов хватает.

Это ты ещё про эмоджи не знаешь. Там есть отдельные символы-модификаторы для разных цветов кожи.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Так что длина строки в юникоде – понятие очень растяживое и зависит от контекста.

В глифах - да, могут быть расхождения. Но чаще всего один кодепоинт соответствует одному глифу.

Поэтому в том же Python'е вполне можно сделать

>>> str = "Hello, world!"
>>> print(str[7:])
world!
>>> 
В общем, речь о том, чтобы оно работало хотя бы также как в Python'е, в котором есть функция len():
>>> len("Привет, мир!")
12

saahriktu ★★★★★
() автор топика
Последнее исправление: saahriktu (всего исправлений: 1)
Ответ на: комментарий от saahriktu

Так что длина строки в юникоде – понятие очень растяживое и зависит от контекста.

В глифах - да, могут быть расхождения. Но чаще всего один кодепоинт соответствует одному глифу.

>>> str = "👨🏿"
>>> len(str)
2

Ну как тебе сказать.

hateyoufeel ★★★★★
()
Ответ на: комментарий от alegz

Тот факт, что вообще-то sizeof любого типа — особенность платформы, для кого-то является новостью?

Новостью не является. Также как этот факт не является причиной забивать на разность размера.

dvetutnev
()
Ответ на: комментарий от saahriktu

Если в Си нет строкового типа и строки - массивы отдельных элементов, то один отдельный элемент массива должен вмещать в себя один отдельный символ, иначе и вырисовываются костыли при работе с такими строками.

Ну вот по-вашему é какой-нибудь — это один символ или несколько? А ? Если речь идёт о том, чтобы узнать длину строки ­— то для чего её узнавать? Чтобы выделить под неё место или чтобы вывести на дисплей? Или для чего?

Xenius ★★★★★
()
Ответ на: комментарий от saahriktu

Ну как тебе сказать.

Как бы, юникод не оставляет выбора. Или довольствоваться этим, или возвращаться назад к KOI8-R.

Про что я (и не только я) тебе тут пишу. Термин «длина строки» в юникоде имеет несколько разных значений в зависимости от контекста.

hateyoufeel ★★★★★
()
Ответ на: комментарий от Xenius

или чтобы вывести на дисплей

В т.ч. и для этого, да. В частности, мой Yegyerdye reader (юникодная версия True hackers' reader) выравнивает длину строк именно через wchar_t, поскольку в случае юникода других вариантов без костылей нет.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

несколько разных значений в зависимости от контекста.

Но я говорю про то, которое соответствует той же функции len() в Python'е. При определённых данных некорректно? Минусы UTF-8. Так что, если уж жрать кактус, то жрать со всеми его минусами.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

поскольку в случае юникода других вариантов без костылей нет.

Вообще есть, нужно просто сканировать всю строку и учитывать, сколько знакомест занимает каждый кодпоинт в сочетании с соседними.

В т.ч. и для этого, да.

То есть é и он будет по-разному выводить?

Xenius ★★★★★
()
Ответ на: комментарий от saahriktu

несколько разных значений в зависимости от контекста.

Но я говорю про то, которое соответствует той же функции len() в Python’е. При определённых данных некорректно? Минусы UTF-8. Так что, если уж жрать кактус, то жрать со всеми его минусами.

Оно корректно для всех данных. Просто это не та длина строки, которую ты ожидаешь.

hateyoufeel ★★★★★
()
Ответ на: комментарий от Xenius

нужно просто сканировать всю строку

Повторяю, что это не True way, поскольку в ряде языков есть строковый тип и строковые функции, которые позволяют легко и просто работать с подстроками.

В Си строка - массив, а подстрока - подмассив.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

нужно просто сканировать всю строку

Повторяю, что это не True way, поскольку в ряде языков есть строковый тип и строковые функции, которые позволяют легко и просто работать с подстроками.

В Си строка - массив, а подстрока - подмассив.

Да. И там под капотом происходит сканирование строки.

В Си строка - массив, а подстрока - подмассив.

В C нет строк.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Напоминаю, что UTF-8 для строк по дефолту ввели только в 3-ей версии Python'а. В те времена, когда Гвидо ван Россум вводил функцию len() в Python, она работала именно так, как говорю я, т.е. она не кодепоинты считала, а именно символы. То, что сегодня она именно кодепоинты считает, - это минус UTF-8. В общем, ждём перехода на UTF-32, а до этого приходится довольствоваться такой точностью, поскольку лучше нормального варианта нет.

saahriktu ★★★★★
() автор топика
Последнее исправление: saahriktu (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Да. И там под капотом происходит сканирование строки.

В библиотеке? Тогда это проблема авторов библиотеки, не программиста.

В C нет строк.

Нет строкового типа. А строки есть как массивы. О чём и речь.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

Напоминаю, что UTF-8 для строк по дефолту ввели только в 3-ей версии Python’а.

только

Есличо, Python3 вышел в 2007 году, если мне память не изменяет.

То, что сегодня она именно кодепоинты считает, - это минус UTF-8. В общем, ждём перехода на UTF-32, а до этого приходится довольствоваться такой точностью, поскольку лучше нормального варианта нет.

Прости, а как UTF-32 поможет? Там тоже кодпоинты будут, просто они будут кодироваться иначе.

Нет строкового типа. А строки есть как массивы. О чём и речь.

Нет ни строкового типа, ни нормальной библиотеки для работы со строками. Сишники за 50 лет даже этого не осилили написать.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Есличо, Python3 вышел в 2007 году, если мне память не изменяет.

Так, а первая версия в 1990-х.

Прости, а как UTF-32 поможет? Там тоже кодпоинты будут, просто они будут кодироваться иначе.

А, ну да. Хотя есть надежда на то, что кол-во составного резко уменьшится. Это пока его никто не юзает его не оптимизируют. А когда он начнёт жрать память, то там могут и задуматься зачем чему-то занимать несколько кодепоинтов, когда можно и один. Это сейчас в UTF-8 уже свободных мест мало.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

ни нормальной библиотеки для работы со строками

Ну так не всем нужно работать с эмоджи и разной экзотикой. Хотя Си позволяет и их выводить. Но если их появление ломает алгоритмы, то это можно считать налогом на юникод, поскольку, вон, и в Python'е не всё так гладко.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

Хотя есть надежда на то, что кол-во составного резко уменьшится.

Схрена ли оно уменьшится? Ты путаешь таблицу символов и метод её представления в байтах. Таблица символов называется Unicode и содержит кодпоинты для разных символов, глифов, модификаторов и служебных знаков. Метод представления в байтах (UTF-8/16/32) – это просто то, как именно одни и те же кодпоинты кодируются.

Это сейчас в UTF-8 уже свободных мест мало.

Что ты, мать твою, несёшь? В UTF-8 места неограниченно много, пока памяти хватит это всё говно парсить.

Ну так не всем нужно работать с эмоджи и разной экзотикой.

эмоджи – уже давно не экзотика, есличо.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 4)
Ответ на: комментарий от hateyoufeel

Кол-во чисел, которыми закодированы кодепоинты, в UTF-32 значительно больше чем в UTF-8.

Впрочем, пожалуй, да, пора выпускать Python 4, в котором либо len() будет кол-во глифов возвращать, либо будет переименована в какую-нибудь ccodepoints(), либо будет вообще выпилена.

А пока такое поведение даже в Python'е, то от языков более низкого уровня, повторяю, и не следует ожидать большего.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

Кол-во чисел, которыми закодированы кодепоинты, в UTF-32 значительно больше чем в UTF-8.

Что это вообще значит?

Впрочем, пожалуй, да, пора выпускать Python 4, в котором либо len() будет кол-во глифов возвращать, либо будет переименована в какую-нибудь ccodepoints(), либо будет вообще выпилена.

Лол нет. Для пистона просто есть нормальная библиотека для работы со строками.

А пока такое поведение даже в Python’е, то от языков более низкого уровня, повторяю, и не следует ожидать большего.

А для C нету.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Библиотеки для работы со строками должны входить в стандарт языка, я считаю. В том же Паскале строковые функции входят в стандартную библиотеку.

Что это вообще значит?

Если почитать стандарт UTF-8, то там есть ограничения на то, как байты представляют номер кодепоинта. В то время как в переменную, которая занимает 4 байта, можно уместить числа в диапазоне 0 - 4294967295. Так что, UTF-32 есть куда расширять.

saahriktu ★★★★★
() автор топика
Последнее исправление: saahriktu (всего исправлений: 1)
Ответ на: комментарий от saahriktu

Если почитать стандарт UTF-8, то там есть ограничения на то, как байты представляют номер кодепоинта

Ага. Только там нет ограничения на размер последовательности байтов под символ. Т.е. сейчас вроде 6 байт максимум, но никто не мешает и дальше копать. Кстати, 6 байт как раз покрывают символы от 0 до 2^31, которые и покрывает UTF-32.

В то время как в переменную, которая занимает 4 байта, можно уместить числа в диапазоне 0 - 4294967295. Так что, UTF-32 есть куда расширять.

Да, только UTF-32 не кодирует кодпоинты от 0 до 2^32. Читай внимательно описание кодировки.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 3)
Ответ на: комментарий от hateyoufeel

сейчас вроде 6 байт максимум

Я слышал, что так было раньше, а потом стало 4 байта максимум. Хотя с тех пор могли опять что-нибудь поменять.

Читай внимательно описание кодировки.

Дык это она сейчас такая. А я говорю, что её ещё могут переделать когда дело дойдёт до практического внедрения во все поля вместо UTF-8.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

сейчас вроде 6 байт максимум

Я слышал, что так было раньше, а потом стало 4 байта максимум. Хотя с тех пор могли опять что-нибудь поменять.

Потому что в юникоде столько кодпоинтов нету. Там только 293168 мест занято пока.

Читай внимательно описание кодировки.

Дык это она сейчас такая. А я говорю, что её ещё могут переделать когда дело дойдёт до практического внедрения во все поля вместо UTF-8.

Шоп английский текст 4 байтами кодировался, 3 из которых – нули? Просто потрясно тупая идея!

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Короче, я нашёл вариант как в Си считать глифы в wchar_t*. Результат корректнее чем у len() в Python'е, поскольку это не просто кол-во кодепоинтов.

Записывайте:

wcswidth(str, wcslen(str))

saahriktu ★★★★★
() автор топика

Стоит добавить еще костылей для кроссплатформенности:

#include <stdio.h>
#include <wchar.h>
#include <locale.h>
 
#if defined(WIN32) || defined(_WIN32) //специальные костыли для виндовой консоли
  #pragma warning(disable : 4996)
  #define _CRT_SECURE_NO_WARNINGS 1
  #include <windows.h>
  #include "fcntl.h"
  #include <io.h>
  void coninit(){
    _setmode(_fileno(stdout), _O_U16TEXT);
    _setmode(_fileno(stdin), _O_U16TEXT);
  }
#else
  #define coninit()
#endif
 
int main(){
  coninit();
  setlocale(LC_ALL, "");
  wchar_t str[100];
  wprintf(L"Проверка ввода текста в консоли (h,в,λ). Введите текст: ");
  wscanf(L"%ls", str);
  for(int i=0; str[i]!=0; i++){
    wprintf(L"[%lc] = 0x%04X\n", str[i], (int)str[i]);
  }
  return 0;
}

Помимо современных систем, работает в win10. А вот в winXP уже нет. Костыли не всесильны. Костыли придуманы при вот этом обсуждении на форуме: https://www.cyberforum.ru/c-beginners/thread3109435-page4.html#post16991735

COKPOWEHEU
()
Последнее исправление: COKPOWEHEU (всего исправлений: 1)
Ответ на: комментарий от dvetutnev

wchar_t плохая затея, как раз из-за разной разрядности

по идее в нем хранятся символы внутри программы, а для общения с пользователем это всё конвертируется по настройкам локали

зачем передавать wchar_t?

разной разрядности

кстати тоже самое можно сказать и про int, никто же я надеюсь инты в файлы не пишет и по сети не передаёт?

sergej ★★★★★
()
Ответ на: комментарий от saahriktu

Только ты сожрал по 4 байта на глиф -_- Сишка это просто что-то с чем-то: адепты то и дело что орут что скорость выполнения и низкое потребление памяти, а на деле то списки вместо hashmap’а, то памяти в четыре раза больше чем надо выделят. Сказочные просто.

cumvillain
()
Ответ на: комментарий от saahriktu

Юникод жирный. Ничего не поделать. Хотите меньше жира - юзайте KOI8-R.

Нет, это ты жирный. Юникод нормальный, просто ты не умеешь со строками в C работать. Нормальные поцоны берут char * и применяют к нему нужные функции.

cumvillain
()
Ответ на: комментарий от cumvillain

С char* работают однобайтные функции, не юникодные. Юникодные функции работают с wchar_t*. Более того, у вас в char* один глиф может также занимать 4 байта и больше, особенно если символ составной. Да, в char* юникодная строка может меньше места занимать, но это как архив. В том смысле, что это не то, с чем работают. Чтобы работать с содержимым архива архив надо разархивировать. Вот в wchar_t* и есть то самое разархивированное состояние строки, с которым можно работать.

saahriktu ★★★★★
() автор топика
Последнее исправление: saahriktu (всего исправлений: 3)
Ответ на: комментарий от cumvillain

Если там char*, то git просто перекладывает строки. Это совершенно другое. Работа со строками начинается с извлечения подстрок, их модификации,... и т.д. Т.е., в контексте вышеупомянутых архивов, перекладывание архивов не есть работа с содержимым архивов.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от sergej

а хешмап - это какая-то волшебная структура, поддерживаемая железом или реализована через такие же списки, массивы и т.д.?

Хешмап это структура которой нет в стандартной сишной библиотеке, поэтому сишные пердоли вместо удобной структуры пердолятся перебором по списку.

cumvillain
()
Ответ на: комментарий от saahriktu

Если там char*, то git просто перекладывает строки. Это совершенно другое. Работа со строками начинается с извлечения подстрок, их модификации,… и т.д. Т.е., в контексте вышеупомянутых архивов, перекладывание архивов не есть работа с содержимым архивов.

Да-да, там все это есть.

cumvillain
()