Я делаю библиотеку, которая позволяет пользователю вставлять и искать пары ключ-значение в виде структуры данных trie. Когда я вставляю строку Unicode, она разбивается на 4 символа (utf-8) (что нормально), но каждый символ становится ‘?’. Поэтому я попытался использовать setlocale(LC_ALL, "")
, но это не сработало (или, может быть, я просто не знаю, какие аргументы подходят для моего случая и где их вызывать). Я не особо забочусь о печати или чтении символа как такового. Все, что я хочу, это то, что это можно каким-то образом представить уникальным образом.
В моем трее есть ссылки вроде node *next[256]
.
Итак, все, что я хочу, это когда вставляется строка Unicode, она вставляется как уникальная комбинация, которая позволяет искать эту строку уникальным образом. Также мне нужен способ определить, что символ Юникода был разбит на 4 отдельных символа. Это потому, что, например, если в строке «wxyz» символ юникода «x» разбит на a, b, c, d, тогда в trie будет сохранено «wabcdyz». Но если бы я на самом деле искал строку wabcdyz (не unicode) , то он найдет запись для этой строки, но это будет несоответствие.
Вот программа, которая показывает, что символ Юникода разбивается на четыре символа ?
:
#include <stdio.h>
int main()
{
printf("Hello World");
char a[] = "Ƃ";
int i;
for(i = 0 ; a[i] != '\0' ; ++i)
{
printf("%c", a[i]);
}
return 0;
}
@IgorGalczak Символы UTF-8 могут иметь размер от одного до четырех байтов (если вы соблюдаете искусственное ограничение Unicode кодовыми точками 0x10FFFF) или от одного до шести (если вы придерживаетесь исходного определения UTF-8). Я подозреваю, что OP на самом деле получает UTF-16 или -32.
@Mihir По задумке каждый байт многобайтовой последовательности UTF-8 имеет свой старший бит, поэтому они никогда не могут конфликтовать с символами ASCII. Пока ваша структура trie не забивает старший бит, она должна работать со строками UTF-8 точно так же, как и с ASCII, даже без явного знания кодировки. Однако, если ваши строки Unicode последовательно имеют четыре байта на символ, они, вероятно, не UTF-8, а UTF-32, с которыми гораздо сложнее работать. Пожалуйста, сообщите нам числовое значение каждого из четырех байтов в одном из проблемных символов.
Однако, с другой стороны, каждый байт с очищенным старшим битом является допустимой однобайтовой кодовой последовательностью UTF-8, и любое количество таких байтов, соединенных вместе, является допустимым текстом в кодировке utf-8. Кроме того, существует множество последовательностей байтов, содержащих некоторые из них с установленным старшим битом, которые не являются допустимыми последовательностями кода UTF-8.
@zwol, понятно, все, что мне нужно, это чтобы один из этих символов Юникода мог быть представлен как один из символов от 0 до 256, потому что именно так моя попытка устанавливает ссылки. Он берет символ, преобразует его в числовое значение и соответственно переходит к следующей ссылке. Мне просто нужен самый точный и быстрый способ добиться этого.
"Строка Юникода"? В C у вас есть кодировка. Так вы имеете в виду "UTF-8"? Или вы делаете, как Python, интерфейс Unicode, и вы обрабатываете (и скрываете) детали?
@Mihir, я заметил, что вы говорите «Unicode», но помечаете [utf-8] и описываете поведение, которое не соответствует UTF-8. UTF-8 — это в одну сторону представления текста Unicode, но не единственный способ.
@Mihir Невозможно преобразовать произвольный символ Юникода в число в [0, 255]
без потери информации из-за принцип сортировки. Но ничего не пойдет не так, если вы дадите каждому байту многобайтовой последовательности UTF-8 свой собственный узел в дереве.
@zwol, каждый байт в многобайтовой последовательности utf-8 получит узел в дереве. Одна проблема, с которой я все еще сталкиваюсь, заключается в том, что даже после setlocale()
он все еще читает символы Юникода, такие как ‘?’ ‘? ‘?’ ‘?’. Как мне заставить его читать правильный символ?
@Mihir Два вопроса: (1) Почему ваша тестовая программа использует int a[] = "Ƃ"
? Это определенно неправильно. Он даже не скомпилируется на моем компьютере. (2) Что выведет ваша программа, на твоем компьютере, если вы замените %c
на %02x
в вызове printf
?
Если я изменю тип элемента a
на char
в представленном коде, он отлично скомпилируется на моей машине и напечатает ожидаемый символ при запуске. Однако я отмечаю, что символ Ƃ
не является членом базового набора исходных или исполняемых символов C. Реализация решает, принимать ли этот символ в виде самого себя в исходном коде C и чему он соответствует во время выполнения.
@zwol, я исправил, это было по ошибке.
@Mihir Все еще жду числовых значений этих байтов...
@zwol, это печатает ffffffc6
@zwol, может быть, теперь я немного понимаю, ffffffc6 разбивается на индекс «-58», а такого индекса нет. Означает ли это, что каждый узел в последовательности utf-8 не находится в диапазоне 0-255?
@JohnBollinger, компилятор общего назначения, который не допускает Unicode в исходном коде, может быть совместимым, но это не делает его менее сломанным и бесполезным. Просто выбросьте его и получите тот, который делает правильные вещи. Дело не в том, что там какой-то дефицит.
@Mihir Для меня это выглядит как нежелательное расширение знака, а не фактические байты 0xFF в вашем строковом литерале. Что будет напечатано, если вы измените всю строку printf
на printf("%02x", (unsigned int)(unsigned char)a[i]);
?
@zwol, теперь c6. Ну наконец-то моя программа работает. Это было причиной того, что моя структура данных терпела неудачу. когда я изменил его на unsigned char *str
, он работает правильно. Большое вам спасибо за вашу помощь.
@Mihir Предупреждение: шестнадцатеричный C6 - это только символ первый многобайтовой последовательности, составляющей op Ƃ. Вы также должны получить второй байт со значением hex 82.
UTF-8 — это механизм для последовательностей символов Юникода кодирование в виде последовательностей байтов, но не единственный способ. Unicode не подразумевает UTF-8, и технически UTF-8 также не подразумевает Unicode.
When I insert a unicode string, it breaks down into 4 characters(utf-8)
Это функция того, как вы храните строковые данные, и
So all I want is when a unicode string gets inserted, it gets inserted as a unique combination which would make it possible to search that string uniquely.
Это относительно просто: кодируйте все свои строки одинаково. Кодирование всех их в UTF-8 было бы моим выбором, но вы также можете использовать любую другую кодировку без сохранения состояния, которая поддерживает все символы, которые могут появляться в ваших строках, например UTF-16 или UTF-32. Но вы должны использовать согласованную кодировку для всех символов всех строк.
Сделав это правильно, вам не обязательно делать что-то еще специальное, чтобы ваше дерево работало. Однако, если вы выберете UTF-16 или UTF-32, я бы предложил структурировать дерево вокруг размера их кодовых единиц. (16- или 32-битные соответственно). В этом нет необходимости, но, скорее всего, это даст преимущества в виде более мелких и, следовательно, более эффективных попыток.
* Обратите внимание, однако, что кодовые единицы UTF-16 и UTF-32 включают множество охватывающих байтов со значением 0, например 0x0031 и 0x00000200. Если вы рассматриваете их как последовательности байтов, а не как последовательности кодовых единиц, то вы должны учитывать это. В частности, вы должны избегать предположения, что отдельные нулевые байты служат терминаторами.
Это отвечает на большую часть моего вопроса. Но как мне вообще включить utf-8? например, я передаю Ƃ как строку для вставки. Когда я просматриваю строковый символ с помощью char, он читается как ‘?’ ‘? ‘?’ ‘?’. Как сделать так, чтобы он правильно читался?
@Mihir Мы действительно не можем вам больше помочь, пока вы не сообщите нам числовое значение каждого из этих четырех байтов. Я могу сказать вам прямо сейчас, что Ƃ
, закодированный в UTF-8, должен создавать последовательность двухбайтовый 0xC6 0x82, так что это делает практически уверенным, что вы нет получаете UTF-8, но я до сих пор не знаю, что вы получаете являются. Также было бы полезно, если бы вы показали нам точную конструкцию кода, которую вы подразумеваете под «Я передаю Ƃ как строку для вставки».
@JohnBollinger Вероятно, вам также следует упомянуть, что UTF-16 и -32 несовместимы с использованием однобайтовых \0
разделителей строк C; вместо этого вы можете использовать U+0000, но тогда вам придется работать с единицами кода, а не с байтами.
@Mihir О, кроме того, нам нужно знать, используете ли вы Windows или нет. Ответ на ваши вопросы setlocale
меняется в зависимости от этого.
@zwol, я использую macOS. У меня есть такая строка, char str[] = “ Ƃ”;
. если я перебираю каждый символ в этой строке до тех пор, пока не встретится «\ 0», он будет повторяться 4 раза и печатать «?» 4 раза. Так просто, он не разбивает символ Юникода правильно, вместо этого он производит «?». Извините, я новичок в этом деле. Я сделаю короткую программу, чтобы изобразить мою проблему и размещу ссылку здесь.
@Mihir, если предположить, что ваш компилятор соответствует C11 (для чего может потребоваться параметр командной строки), хорошей ставкой для литералов, как вы описываете, является использование литералов utf8: char str[] = u8"Ƃ";
. Однако для данных, считанных из внешних источников, вам нужно либо предположить, что они будут закодированы в UTF-8, либо вы должны быть готовы перекодировать их в UTF-8 (что требует знания и поддержки исходной кодировки).
Я попробовал это на некоторых онлайн-компиляторах, и они выдают разные результаты. На моей машине это напечатало бы '?' 4 раза
@Mihir Mihir Я скопировал твою программу в твой вопрос. В будущем, пожалуйста, предоставьте пример кода, отредактировав вопрос, а не с помощью pastebin; мы хотим, чтобы вопросы и ответы имели смысл через много лет, а это не работает, если они зависят от ссылок на внешние сайты, которые уже могут быть недействительными. (Знаете ли вы, что можете редактировать свой вопрос? Крохотное серое слово edit
под ссылками — это кнопка. Да, действительно. Да, это плохой дизайн пользовательского интерфейса. Извините.)
@zwol, мне очень жаль. Мне в голову не пришло. Кроме того, всего за 1 неделю я присоединился к переполнению стека. Я обязательно сделаю это правильно в будущем
В любом случае, @Mihir, то, что печатается на экране, не соответствует тому, что вы видите в исходном файле, отражает несоответствие между предполагаемой и фактической кодировкой. Если это происходит, когда вы не испортили локаль, то это говорит о том, что фактическая кодировка вашего исходного файла отличается от того, что ожидает компилятор. На стороне вывода также может быть дополнительное несоответствие. В любом случае взаимодействующие компоненты должны знать и соглашаться с используемыми кодировками.
@Михир Не беспокойся. Мы все когда-то были новичками на этом сайте.
По моему опыту символ utf-8 представлялся в 2 байта, 2 символа. Первый байт Utf начинается с 0xC0 - en.wikipedia.org/wiki/UTF-8, поэтому вы должны хранить символы в байтах (uint8/signed char)