Разделить строку, содержащую символы разных размеров

У меня есть входная строка, содержащая символы разных размеров, например const char * input = "aadđ€€¢¢". strlen дал результат 15, что означает, что в то время как «aad» занимает всего 3 байта, другие специальные символы занимают по 2 или более байта каждый.

Как я могу вырезать символы, которые помещаются в 6 байтов от начала этой строки? Это означает, что в этом случае будет использоваться только «aadđ», потому что aadđ€ будет занимать 8 байтов.

Я пробовал обычные методы разделения символов, но пока ни один из них не работал. Редактировать: потому что широкий символ может быть разделен посередине, и поэтому вместо этого я получу какой-то мусор или другой символ.

Вы используете строку в стиле C. Ваш вопрос тогда о языке C? C или C++: выберите один.

John Bollinger 30.05.2019 14:10

Привет, я бы с удовольствием принял решение и на C++. Но я работаю с ограниченной средой, поэтому принимаются только стандартные библиотеки.

An Phong 30.05.2019 14:32

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

Mgetz 30.05.2019 14:37

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

Fureeish 30.05.2019 14:39

Связано с предположением, что вы можете использовать пробел для разделения stackoverflow.com/q/236129/332733

Mgetz 30.05.2019 14:43

Вопрос кодировки касается «-fexec-charset» вашей компиляции или эквивалента. ("-fsource-charset" - это не информационный вопрос, это просто фундаментальное требование.)

Tom Blodget 30.05.2019 18:59

Спасибо вам, ребята. Ввод поступает со смартфона на другое устройство, поэтому я не знал, как с этим справиться. Кодировка - UTF-8, поэтому я почитаю об этом подробнее, чтобы решить эту проблему.

An Phong 31.05.2019 04:51

Я проголосовал за повторное открытие, потому что этот вопрос явно не об отладке. В нем также есть четкая постановка проблемы: «Как я могу вырезать символы, которые умещаются в 6 байтов, от начала этой строки?»

Olaf Dietsche 01.06.2019 13:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
8
146
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

char input2[7] = {0};
memcpy(input2, input, 6);

Если вы хотите получить длину wchar, вы можете использовать wcslen()

http://www.cplusplus.com/reference/cwchar/wcslen/

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

An Phong 30.05.2019 14:27

Здесь нельзя использовать wcslen(). Широкие символы не являются чудодейственным решением для многобайтовых кодировок.

Fureeish 30.05.2019 14:38
Ответ принят как подходящий

strlen считает байты, а не символы. Чтобы пройти по строке посимвольно, вы можете попробовать mblen, который просматривает следующий символ в строке. Если кодирование строки не является UTF-8, вы должны соответствующим образом скорректировать вызов на setlocale.

std::setlocale(LC_ALL, "en_US.utf8");
const char *input = "aadđ€€¢¢";
int clen;
mblen(0, 0);
for (const char *p = input; *p != 0; p += clen) {
    clen = mblen(p, 4);
    std::cout << p << ", clen = " << clen << '\n';
}

Получить ровно 6 байтов может оказаться сложно, потому что это может остановиться на полпути в многобайтовом символе.

int len = 0, clen;
mblen(0, 0);
for (const char *p = input; *p != 0 && len < 6; p += clen, len += clen) {
    clen = mblen(p, 4);
}

char buf[10];
strncpy(buf, input, len);
buf[len] = 0;

Это остановится, как только будет достигнуто 6 байтов или больше.

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

if (len > 6)
    len -= clen;

Спасибо, Олаф. Ваше решение решило мою проблему.

An Phong 31.05.2019 05:41

Вам нужно понимать разницу между «байтами» и «символами».

Байт — наименьшая единица памяти компьютера, содержит 8 бит информации. Символ (точнее, кодовая точка Unicode) — это число от 0 до 0x10FFFF, представленное одним или несколькими байтами, в зависимости от используемой кодировки. Символ связан с некоторым «глифом», изображением, которое является частью различных шрифтов.

Символы с кодами от 0 до 127 (обычно называемые «символами ASCII», но технически называемые блоками «Элементы управления C0 и базовая латынь») кодируются одним байтом. К ним относятся английские буквы, цифры и некоторые знаки препинания. Остальные символы кодируются несколькими байтами. Пожалуйста, посмотрите UTF-8 и UTF-16 для некоторых примеров того, как делается кодировка.

Чтобы ответить на ваш вопрос, учитывая строку в вашем примере, вы можете вырезать 6 байты в начале строки, но последние байты могут не представлять допустимый символ. В UTF-8 это будет «префиксный» байт, за которым будет следовать от одного до трех байтов, чтобы сформировать полную кодовую точку.

UTF-8, UTF-16 и UTF-32 — это кодировки одного и того же Unicode кодовые точки. Из-за композиции может потребоваться более одной кодовой точки для представления одного глиф, который вы бы распознали как символ. Выяснение этой последней части — задача для такой библиотеки, как ICU. Для большинства простых целей вы можете довольно легко проверить длину байтов для кодовых точек UTF-8. Почитайте про кодировку в Википедии.

Khouri Giordano 30.05.2019 15:20

@KhouriGiordano Вы правы, я совсем забыл о композиции. Я не уверен, что добавление еще одного уровня абстракции улучшит понимание ОП :). Я изменю ответ на вики сообщества, пожалуйста, не стесняйтесь редактировать. Или предоставить свой собственный, конечно.

user3458 30.05.2019 18:28

«Обычно называется «символами ASCII», но это не совсем правильное название»: верно, в контексте набора символов Unicode это блок Элементы управления C0 и базовая латынь.

Tom Blodget 30.05.2019 19:02

Спасибо. Я посмотрю UTF-8, чтобы понять, как делается кодировка для решения этой проблемы.

An Phong 31.05.2019 04:52

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