LINUX.ORG.RU

Юникод и перемещение указателя строки на N символов

 


3

2

На ЛОРе многие активно продвигают юникод, пытаясь убеждать, что это универсально и современно, и не так уж много ест ресурсов. Но, разве можно нормально работать с подстроками в юникоде? Выношу вопрос отдельно, поскольку интересно посмотреть практическое решение, а не только утверждения, что это можно делать специализированными функциями. Глянул я эту документацию по wchar.h и так ничего и не понял.

Как сдвинуть указатель на N символов? На N байт указатель передвинуть проще пареной репы:

strptr + N
А как передвинуть указатель на строку в юникоде на N юникодных символов специализированными функциями?

★★★★★

Ответ на: комментарий от deep-purple

Оно считает это тремя символами, что, вообще-то корректно.

Символ один, состоит из 3 кодпойнтов. Надо бы проверить в свифте, там говорят реализовано правильно, но у меня нет макбука.

PolarFox ★★★★★
()
Ответ на: комментарий от no-such-file

Но они разные, и на вид и на состав:

$ ./test é̤ é̤
é̤ 3 // эта из console.log()
é̤ 2 // эта из педикеи
И сегодня я уже во сне вспомнил написать. Есть же в регулярках специально для матча такая вот хреновина:
/\p{L}\p{M}/
где «L» это леттерс, а «M» это вот эти хвостики.

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

С таким раскладом я могу еще пару тыщ выпердышей напридумывать соединяя «кодпойнты» в «один символ». Т.е. в целом, я не против этих извращений, но мне плевать какие они существуют и какие еще завтра придумают. Главное — я не отрежу пол символа кодпоинта.

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

Ну и? В реальности это 1 буква/символ. Юникод передаёт её 2 или 3 кодпоинтами (отдельный ЛОЛ), именно это я тебе и втолковываю. Нельзя перемещаться по UTF-32 строке на 1 букву вперед/назад просто по кодпоинтам, как в классической однобайтной кодировке.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

UTF-32

Ты у ж определись о чем мы говорим, о юникоде вообще, или о утф-8 в частности. Я о последнем. Кроме того, что значит нельзя? Главное что вопросы в ромбиках не выскакивают.

Ну и к отдельному лолу — есть и такие же однопойнтовые символы. Но не для всех, да, ибо для всех не наделаешь, ато:

я могу еще пару тыщ выпердышей напридумывать соединяя «кодпойнты» в «один символ»

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

Извините, что влезаю. Я тут начал «просвещаться» по этой теме, вот тут очень хорошо описано: http://utf8everywhere.org/

Более конкретно, цитата:

User-perceived character
Whatever the end user thinks of as a character. This notion is language dependent. For instance, ‘ch’ is two letters in English and Latin, but considered to be one letter in Czech and Slovak.

...

When a programming language or a library documentation says ‘character’, it typically means a code unit. When an end user is asked about the number of characters in a string, he will count the user-perceived characters. A programmer might count characters as code units, code points, or grapheme clusters, according to the level of the programmer’s Unicode expertise.

И отсылка к Twitter'у, о том как он считает свои 140 символов: https://dev.twitter.com/docs/counting-characters

gh0stwizard ★★★★★
()
Ответ на: комментарий от deep-purple

Ты у ж определись о чем мы говорим, о юникоде вообще

Конечно вообще. Деление на кодпоинты не зависит от конкретной системы кодирования. Что utf-8, что ucs-4 - один хрен эта супербуква кодируется несколькими кодпоинтами.

Я о последнем

Про него и говорить не стоит, у него же переменная длина символа по определению. Я говорю, что в юникоде в принципе, by design, нет такой системы кодирования, чтобы все символы были одной длины в байтах. Так он устроен.

Главное что вопросы в ромбиках не выскакивают

У тебя есть слово, в слове нужно взять третью букву, а ты со смещением на N*k кодпоинтов вместо Ё берёшь точки от Ё. Весело, да.

Другое дело, что сама по себе такая операция нахрен не сдалась, т.к. если строка и обрабатывается посимвольно, то обычно все символы, а не какой-то 1 из середины. Если такое всё-таки надо, то строку придётся хранить не в массиве, а в списке, где каждый элемент - массив кодпоинтов представляющий 1 символ. Заодно облегчается наворачивание этих самых композитных «симовлов», т.е. если например у нас есть символ «e», то к нему можно добавлять разные циркумфлексы-акуты без передвигания массива.

no-such-file ★★★★★
()
Ответ на: комментарий от Legioner

С wchar_t — во-первых не гарантируется, что он 4-байтовый

Не так. Цитата из http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

3.7.3
wide character
bit representation that fits in an object of type wchar_t, capable of representing any character in the current locale

См. стр. 5. Т.о. на тех системах, где компилятор дает 2 байта нет поддержки Unicode в принципе. Либо компилятор чуть менее, чем полностью кривой.

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

Т.о. на тех системах, где компилятор дает 2 байта нет поддержки Unicode в принципе.

Ну что значит «нет поддержки Юникода»? wchar_t должен вмещать «любой символ, представимый в самой большой кодировке среди доступных локалей», это правда, и это вовсе не обязан быть весь Юникод, и это вообще может быть кодировка, отличная от Юникода. Но тем не менее, обрабатывать тексты в Юникоде (UTF-8, UTF-16 и UTF-32) можно на машине Тьюринга.

Кроме того, все коды Юникода (от 0 до 0x10FFFF) влезают в 21 бит.

ибо компилятор чуть менее, чем полностью кривой.

В MS Visual Studio wchar_t имеет длину 16 бит.

По этим двум причинам и придумали char16_t и char32_t.

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

Ну что значит «нет поддержки Юникода»?

Ну, то и значит. UCS-2 != Unicode. Цитирую:

As of Unicode 8.0 there are 120,520 graphic characters.

https://en.wikipedia.org/wiki/Unicode
Что было до 2000 года (если что, сейчас 2016 год):

The UCS has over 1.1 million code points available for use, but only the first 65,536 (the Basic Multilingual Plane, or BMP) had entered into common use before 2000.

https://en.wikipedia.org/wiki/Universal_Coded_Character_Set

По этим двум причинам и придумали char16_t и char32_t.

Вот, что написано в unicode/umachine.h (libicu):

/* UChar and UChar32 definitions -------------------------------------------- */

/** Number of bytes in a UChar. @stable ICU 2.0 */
#define U_SIZEOF_UCHAR 2

/**
 * \var UChar
 * Define UChar to be UCHAR_TYPE, if that is #defined (for example, to char16_t),
 * or wchar_t if that is 16 bits wide; always assumed to be unsigned.
 * If neither is available, then define UChar to be uint16_t.
 *
 * This makes the definition of UChar platform-dependent
 * but allows direct string type compatibility with platforms with
 * 16-bit wchar_t types.
 *
 * @stable ICU 4.4
 */
#if defined(UCHAR_TYPE)
    typedef UCHAR_TYPE UChar;
/* Not #elif U_HAVE_CHAR16_T -- because that is type-incompatible with pre-C++11 callers
    typedef char16_t UChar;  */
#elif U_SIZEOF_WCHAR_T==2
    typedef wchar_t UChar;
#elif defined(__CHAR16_TYPE__)
    typedef __CHAR16_TYPE__ UChar;
#else
    typedef uint16_t UChar;
#endif

/**
 * Define UChar32 as a type for single Unicode code points.
 * UChar32 is a signed 32-bit integer (same as int32_t).
 *
 * The Unicode code point range is 0..0x10ffff.
 * All other values (negative or >=0x110000) are illegal as Unicode code points.
 * They may be used as sentinel values to indicate "done", "error"
 * or similar non-code point conditions.
 *
 * Before ICU 2.4 (Jitterbug 2146), UChar32 was defined
 * to be wchar_t if that is 32 bits wide (wchar_t may be signed or unsigned)
 * or else to be uint32_t.
 * That is, the definition of UChar32 was platform-dependent.
 *
 * @see U_SENTINEL
 * @stable ICU 2.4
 */
typedef int32_t UChar32;
Особенно интересно замечание про unsigned/signed. Вот что у gcc-5.3.0 + musl-1.1.12:
/usr/include/uchar.h:7:typedef unsigned short char16_t;
/usr/include/uchar.h:8:typedef unsigned char32_t;
На wheezy, gcc-4.7.2 + eglibc-2.13 значения для char16_t, char32_t вообще не определены для сишки.

Может чего не догоняю, но использовать char16_t, char32_t также противопоказано. По-хорошему, надо писать свою либу или использовать готовую. Одними только типами ничего не добиться.

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

Я не совсем понял, при чём здесь UCS-2 и для чего ты скопипастил кусок из ICU.

wchar_t, как и char, может быть как со знаком, так и без. char16_t и char32_t введены в C11/C++11. Они должны быть без знака и не меньше 16 и 32 бит соответственно (по-моему, они должны быть одинакового размера и выравнивания с uint16_least_t и uint32_least_t, но не уверен). Если у GCC-5.3.0+MUSL-1.1.12 unsigned short = 16 бит, а unsigned int = 32 бита, то ничто не мешает разработчиком определить char16_t и char32_t как аналогичные им, что они и сделали.

Но, конечно, так как ICU разрабатывался задолго до C++11, там свой тип данных для code points, выбираемый в зависимости от платформы, да.

По-хорошему, надо писать свою либу или использовать готовую. Одними только типами ничего не добиться.

Это правда, но ТСу же очень надо. Тем более, что в C++ многие шаблонные типы нормально работают с char16_t и char32_t.

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

wchar_t, как и char, может быть как со знаком, так и без.

А в юникоде: «The Unicode code point range is 0..0x10ffff».

Это правда, но ТСу же очень надо.

Ну, пусть тогда решает сам. «Пищу» я скинул. Да, может я не прав, а ты прав.

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

А в юникоде: «The Unicode code point range is 0..0x10ffff».

Ну ведь 0x10FFFF вполне можно представить в виде числа со знаком (32 бита, например).

Я, в первую очередь, хотел сказать, что wchar_t был придуман без ориентации на Юникод и все коды Юникода вмещать не обязан, поэтому его и не рекомендуют использовать в голом виде.

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

wchar_t был придуман без ориентации на Юникод

Эх. Допустим завтра для юникода нужно будет 5 байт. И тут-то wchar_t решает :) Просто никто не захотел доводить до ума этот вопрос в компиляторах/библиотеках и т.д. Т.е. стандарт тупо опережал время. Сейчас-то уже известно что и как.

и все коды Юникода вмещать не обязан

Он должен вмещать все символы локали. Если локаль отображает лишь 65к символов, то в ней нет поддержки юникода.

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

дык, есть. например ICU (http://site.icu-project.org/home). там есть поддержка всего и вся. со всеми свистелками и перделками, какие только можно вообразить. главное, не захлебнуться в обилии возможностей. юникодов много, врукопашную это писать геморно. но ещё геморнее поддерживать кучу разных кодировок, настроек консоли, всяких форматов вывода в текстовые файлы и систем интернационализаций. а есть ещё всякие там китайцы, которые вообще иероглифами пользуются. так что юникод - это добро, хотя и несколько неудобоваримое.

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

Эх. Допустим завтра для юникода нужно будет 5 байт.

Это крайне маловероятно, разве что японцы придумают миллион новых эмодзи или инопланетяне прилетят. «От 0 до 0x10FFFF» — это теоретические пределы по стандарту, из них реально присвоено абстрактным символам менее 6/17 диапазона (более 2/17 под private use), остальные свободны. Если этот диапазон расширится, будут серьёзные проблемы в любом случае, как при введении суррогатных пар.

Он должен вмещать все символы локали. Если локаль отображает лишь 65к символов, то в ней нет поддержки юникода.

Это правда, но если ты не будешь обрабатывать строки функциями из стандартной библиотеки (которые, как ты сам сказал, для Юникода обычно не подходят) и ввод/вывод будет на уровне байт, какая тебе разница, какая там локаль?

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

ввод/вывод будет на уровне байт

WUT? Для юникода нет понятия уровень байт, есть понятия code point, code unit (вид упаковки, можно же в struct{ byte, byte, byte, byte; }).

какая тебе разница, какая там локаль?

Так написано в стандарте! Т.е. это дело принципа. Если мне компилятор выплевывает sizeof(wchar_t) == 2, то я сразу понимаю, что с юникодом можно не заморачиваться. В контексте кросс-платформ кода, это значит, что надо конвертировать в какой-то codepage перед выводом на экран. Т.к. всеравно на экране пользователь не увидит символ за пределом 65,535! И да, это надо описывать, что возможно, будут потери отображения. Да, возможно, что-то не сможете ввести. И это нормально. ИМХО. Ненормально пытаться выводить символы, которая система в принципе (!) не может выводить.

Так что, то что показывает MSVS это истина. Такая уж кривая винда. И заморочки с переопределением main() сделаны были не зря. То, что выбрали UTF-16 вместо UTF-8 или UTF-32 это другой вопрос. Кроме UTF-16 туда же добавили хаки на main(), fopen() и т.д. Т.е. то, что на линухе fopen() + char32_t отработает не значит, что он также идеально будет пахать на винде.

Выше я дал линк на utf8 everywhere, там виндузятник все описал, что нужно делать под винду в этом случае. И на какие грабли можно напороться тоже (конвертирование строковых констант).

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

Для юникода нет понятия уровень байт,

Как так нету? UTF-8, UTF-16 и UTF-32 описаны в Unicode Core Specification (хотя также и в отдельных документах).

Т.к. всеравно на экране пользователь не увидит символ за пределом 65,535!

Разумеется, если надо выводить символы на экран, то будут проблемы.

Но если тебе надо их только выводить в файл, передавать по сети, или у тебя своя графическая библиотека, которая выводит на экран пиксели или векторные примитивы, то ты можешь на какой угодно системе работать с Юникодом.

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

Но если тебе надо их только выводить в файл, передавать по сети, или у тебя своя графическая библиотека, которая выводит на экран пиксели или векторные примитивы, то ты можешь на какой угодно системе работать с Юникодом.

В таком контексте можно думать и о wchar_t в 2 байта. Типа, поддержка только латиницы + евразии (забив на Китай, Японию и т.д.).

P.S. Логи-то ты как смотреть собрался? =)

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

В таком контексте можно думать и о wchar_t в 2 байта. Типа, поддержка только латиницы + евразии (забив на Китай, Японию и т.д.).

Не понимаю, какая связь.

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

Такая связь, что если вдруг приспичит вывести в консоль, то получишь обламинго. Представим, что у нас не винда с GUI, а какой-то древний AIX, сервер, и у тебя лишь «чорная» консоль, клава и логи на экране. А wchar_t в 2 байта. Придется ж писать конвертор, да? Но, ты-то думал, что раз либа, то либа не выводит ничего и никогда на экран...

Эх. Всё. Думаю, ТС разберется сам.

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

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

Тогда, конечно, получишь.

Но, ты-то думал, что раз либа, то либа не выводит ничего и никогда на экран...

«На экран» и «в консоль» — это разные вещи. Если либа что-то выводит в консоль, а консоль без Юникода, то вывести Юникод в консоль нельзя будет, безусловно.

Эх. Всё. Думаю, ТС разберется сам.

Я думаю, в него просто вселился дух Эдди, и он на самом деле только потроллить хотел, а разбираться всё равно не будет.

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

отсылка к Twitter'у
Tweet length is measured by the number of codepoints in the NFC normalized version of the text.

Ну вот я об этом. Нормализации нет, посчитал как есть их того что подсунули и все. Для меня главное не порезать кодпоинт, а сколько их там — не важно.

deep-purple ★★★★★
()
Ответ на: комментарий от no-such-file

нет такой системы кодирования, чтобы все символы были одной длины в байтах

Зато есть длина в кодпоинтах, которые ты попутал тут с байтами. Один кодпоинт может быть размером от 1 до 6 байт.

ты со смещением на N*k кодпоинтов вместо Ё берёшь точки от Ё

У меня не стояла задача заниматься нормализацией. А если бы и стояла, то я бы не велосипедил и взял бы ту мегалибу что советовали выше. Правда, чую, и она не будет работать корректно на все 100% в какой-то момент т.к. это, например, апдейтят постоянно: http://www.unicode.org/Public/UCD/latest/ucd/CompositionExclusions.txt

массив кодпоинтов представляющий 1 символ

Один кодпоинт и представляет один символ, за исключением тех, которые состоят из нескольких кодпоинтов, которые нормализуются через вон те правила что ссылку я скинул выше. Или мы об одном и том же тут с тобой?

если например у нас есть символ «e»

Если бы такая задача стояла изначально, то из исходной строки вообще разово создавалась бы цепочка, которую крути потом как хош.

deep-purple ★★★★★
()
Ответ на: комментарий от proud_anon

Ну ведь 0x10FFFF вполне можно представить в виде числа со знаком (32 бита, например)

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

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

Ну если для твоей задачи отрезание нецелого символа некритично, то ок.

PolarFox ★★★★★
()
Ответ на: комментарий от deep-purple

Один кодпоинт и представляет один символ, за исключением тех, которые состоят из нескольких кодпоинтов, которые нормализуются через вон те правила что ссылку я скинул выше

Ты тупой? Я тебе показал символ, который состоит из 2 кодпоинтов даже после нормализации. Редкий да, но тебе от этого не легче.

которые ты попутал тут с байтами

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

Зато есть длина в кодпоинтах, которые ты попутал тут с байтами

Нету, т.к. неизвестно, есть ли в строке символы из нескольких кодпоинтов.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

это не возможно

Ну конечно, а та мегалиба, это просто понт с имитацией возможностей.

т.к. неизвестно

Если тебе что-то мешает узнать (e.g. strncmp), то тупой ты.

deep-purple ★★★★★
()
Ответ на: комментарий от no-such-file

ТС спрашивал как ему получить смщение символа в байтах от начала массива

Так массив вполне может быть не в char, а в wchar_t/char16_t/char32_t. Можно и в таких единицах смещение, да, если это возможно. Можно и сразу готовый указатель если есть нечто наподобие

wchar_t          *wcsoffset (wchar_t *str, int chars);
или
char32_t         *u_stroffset32 (char32_t *str, int chars);

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

strncmp

Ох ЛОЛище. Ты что, собрался перебирать все вариации составных символов сравнением? Нет ты не тупой, ты клоун. За сим разговор считаю оконченным.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Цитирую:

e.g.

Нехрен из контекста вырывать. Клоунаду пока тут только ты устроил.

deep-purple ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.