Команда indexOf() не работает в строке с русскими буквами

Мир Ардуинщиков
Offline
Зарегистрирован: 18.05.2020

Всем привет, друзья! У меня есть программа которая находит восклицательный знак в строке и выводит его индекс:

String MyStr = "&привет! мир";

void setup() {
   Serial.begin(115200);
}

void loop() {
   Serial.print("Индекс восклицательного знака: ");
   Serial.println(MyStr.indexOf('!'));
}

По сути, индекс восклицательного знака должен быть равен 7. Но вместо этого он равен числу 13. Кто знает как это можно исправить? Может дело в кодировке русских букв? Буду очень признателен если вы мне подскажите :-)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Зачитай чонить за UTF-8

b707
Offline
Зарегистрирован: 26.05.2017

Мир Ардуинщиков пишет:

индекс восклицательного знака должен быть равен 7. Но вместо этого он равен числу 13. Кто знает как это можно исправить?

не надо это исправлять, это правильный ответ.

Если вы попытаетесь извлечь этот символ методом at() - вы убедитесь. что он таки там на позиции 13, а не 7

Мир Ардуинщиков
Offline
Зарегистрирован: 18.05.2020

Спасибо! Сейчас проверю.

rkit
Offline
Зарегистрирован: 23.11.2016

Этот код работает с байтами, а не символами. Поскольку символ может занимать несколько байт, такие манипуляции не будут работать с кириллицей.

Мир Ардуинщиков
Offline
Зарегистрирован: 18.05.2020

Спасибо за совет! Только как сделать так, чтобы этот скетч мог работать с кириллицей? PS: я слабо разбираюсь в кодировках и методах String

b707
Offline
Зарегистрирован: 26.05.2017

Мир Ардуинщиков пишет:

я слабо разбираюсь в кодировках и методах String

тогда я не знаю, как вам помочь.

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

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

rkit пишет:

Этот код работает с байтами, а не символами. Поскольку символ может занимать несколько байт, такие манипуляции не будут работать с кириллицей.

А мне вот интересно какая птица его минусанула за правильный совет... От жеж клоуны :)

Мир Ардуинщиков
Offline
Зарегистрирован: 18.05.2020

Я вот нашёл ещё один теоретически рабочий способ. Недавно, мне надо было вывести русские буквы на OLED I2C дисплей с шиной SSD1306. Для этого я использовал функцию перекодировки из UTF-8 в Windows-1251. PS: её я нашёл на этом-же форуме:

String utf8rus(String source) {
  int i,k;
  String target;
  unsigned char n;
  char m[2] = { '0', '\0' };
  k = source.length(); i = 0;
  while (i < k) {
    n = source[i]; i++;
    if (n >= 0xC0) {
      switch (n) {
        case 0xD0: {
          n = source[i]; i++;
          if (n == 0x81) { n = 0xA8; break; }
          if (n >= 0x90 && n <= 0xBF) n = n + 0x30;
          break;
        }
        case 0xD1: {
          n = source[i]; i++;
          if (n == 0x91) { n = 0xB8; break; }
          if (n >= 0x80 && n <= 0x8F) n = n + 0x70;
          break;
        }
      }
    }
    m[0] = n; target = target + String(m);
  }
return target;
}

Я попробовал применить эту функцию и о чудо! Всё заработало))) Спасибо человеку под ником arduinec. Вот код который у меня получился:

String MyStr = "&привет! мир"; // создаём переменную типа "String"

void setup() {
   Serial.begin(115200); // указываем скорость работы COM-порта
}

void loop() {
   Serial.print("Индекс восклицательного знака: ");
   MyStr = utf8rus(MyStr); // перекодируем строку из UTF-8 в Windows-1251
   Serial.println(MyStr.indexOf('!')); // выводим индекс восклицательного знака
}

String utf8rus(String source) {
  int i,k;
  String target;
  unsigned char n;
  char m[2] = { '0', '\0' };
  k = source.length(); i = 0;
  while (i < k) {
    n = source[i]; i++;
    if (n >= 0xC0) {
      switch (n) {
        case 0xD0: {
          n = source[i]; i++;
          if (n == 0x81) { n = 0xA8; break; }
          if (n >= 0x90 && n <= 0xBF) n = n + 0x30;
          break;
        }
        case 0xD1: {
          n = source[i]; i++;
          if (n == 0x91) { n = 0xB8; break; }
          if (n >= 0x80 && n <= 0x8F) n = n + 0x70;
          break;
        }
      }
    }
    m[0] = n; target = target + String(m);
  }
return target;
}

Спасибо всем кто мне что-либо советовал

Upper
Offline
Зарегистрирован: 23.06.2020

brokly пишет:

rkit пишет:

Этот код работает с байтами, а не символами. Поскольку символ может занимать несколько байт, такие манипуляции не будут работать с кириллицей.

А мне вот интересно какая птица его минусанула за правильный совет... От жеж клоуны :)

А какой правильный ответ он дал? "такие манипуляции не будут работать с кириллицей"? Так ТС ниже провел таки успешные манипуляции. Ни одного толкового совета озвучено не было.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

только фукцию перекодировки надо объявлять до её первого применения

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Upper пишет:

А какой правильный ответ он дал? "такие манипуляции не будут работать с кириллицей"? Так ТС ниже провел таки успешные манипуляции. Ни одного толкового совета озвучено не было.

Русским по белом написано, что каждый русский символ представлен двумя байтами, то есть номер символа в строке не такой как ты видишь на экране. Че не так то ?

Upper
Offline
Зарегистрирован: 23.06.2020

brokly пишет:

А мне вот интересно какая птица его минусанула за правильный совет... От жеж клоуны :)

"Че не так то ?"

Так совета то и не было. 

У меня у самого мало опыта в Ардуино. И с такой проблемой я раньше не сталкивался, т.к. ковырялся в протеусе и Atmel Studio, и там у работал в 1251. Поэтому эта тема меня заинтересовала. Но дельного совета, что делать - я не увидел. Да это UTF-8. Но что делать? Переходить с UTF8 на 1251 по умолчанию - тогда как? Использовать UTF - но это как минимум в два раза больше памяти и как оказывается (может ошибаюсь) теряется часть функционала String и других библиотек.

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

b707
Offline
Зарегистрирован: 26.05.2017

Upper пишет:

Да это UTF-8. Но что делать? Переходить с UTF8 на 1251 по умолчанию - тогда как? Использовать UTF - но это как минимум в два раза больше памяти

есть третий метод, самый простой - не использовать кириллицу вообще. На латинице все символы по одному байту и методы библиотек работают :)

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Upper пишет:

Так совета то и не было. 

Отнюдь.

Единственно правильный совет был дан сразу - разобраться с кодировками кириллицы.

Цитата:

У меня у самого мало опыта в Ардуино. И с такой проблемой я раньше не сталкивался,

Не удивительно, т.к. проблема никакого отношения к Ардуино не имеет.

Цитата:

Но дельного совета, что делать - я не увидел. Да это UTF-8. Но что делать?

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

Цитата:

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

Как раз наоборот: на от вопрос, который Вы подразумевали, нет и не может быть единственно правильного ответа. В каждом конкретном случае оптимальным является один из множества различных вариантов. А чтобы понять, какой именно оптимален в Вашем случае, нужно хорошо разбираться в кодировках. Что Вам и посоветовали.

Upper
Offline
Зарегистрирован: 23.06.2020

andriano пишет:

Единственно правильный совет был дан сразу - разобраться с кодировками кириллицы.

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

Единственно правильный совет = "Зачитай чонить за UTF-8"

b707
Offline
Зарегистрирован: 26.05.2017

Upper пишет:

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

а какого совета вы ждете? Вы думаете, есть способ, ничего не изучая, разом решить вопрос? Заклинание? "Елики-беники, съели вареники, решись мая проблемка сама сейчас же!" ??

Вот вам еще варианты, как решить проблему, не вникая в суть:

 - отказаться от кириллицы

 - заказать разработку программы знающему программисту

 

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

Upper
Offline
Зарегистрирован: 23.06.2020

b707 пишет:

а какого совета вы ждете? Вы думаете, есть способ, ничего не изучая, разом решить вопрос? Заклинание? "Елики-беники, съели вареники, решись мая проблемка сама сейчас же!" ??

Вот вам еще варианты, как решить проблему, не вникая в суть:

 - отказаться от кириллицы

 - заказать разработку программы знающему программисту

 

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

Да нет у меня проблемы в этом вопросе. С чего вы это взяли? Я выше это все уже писал. Я пользуюсь win-1251 по умолчанию, и мне этого хватает. Мне просто стало интересно, как с этим обстоит в arduino IDE. И первый мой пост в этой теме был о том, почему поставил минус. Никакой помощи не просил.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Upper, можно вопрос? А в атмел студии можно применять функции до их объявления?

Upper
Offline
Зарегистрирован: 23.06.2020

ua6em пишет:

Upper, можно вопрос? А в атмел студии можно применять функции до их объявления?

Атмел студия сама добавляет предварительные объявления функций при импорте скетча ардуино.

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

 

Мир Ардуинщиков
Offline
Зарегистрирован: 18.05.2020

Единственная проблема функции utf8rus() это то, что при выводе переменной MyStr в COM порт мы получаем это:

Индекс восклицательного знака: 7 Переменная MyStr: &⸮⸮⸮⸮⸮⸮! ⸮⸮⸮
 
Конечно, это не такая большая проблема, но её стоит учитывать 
 
 
b707
Offline
Зарегистрирован: 26.05.2017

Мир Ардуинщиков пишет:

Конечно, это не такая большая проблема, но её стоит учитывать 
 
это вообще не проблема функции. а проблема ваших знаний.
Перекодируйте строку не саму в себя, а в другую строчку:
 
NewStr = utf8rus(OldStr);
 
C новой строкой работайте функциями indexOf(), а на печать выводите старую
Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

Господа, а подскажите почему до сих пор у новичков такие проблемы с UTF-8? Не судьба применить другую кодировку из оперы 437, по типу KOI8 там, не? Всё ж придумано до нас ещё на БКшках и спектрумах, когда мы молодого Гейтса с Windows 95 в москвах принимали. Тогда вроде ещё дунди по прилавкам в дефиците лежала, и шваль всякая бритоголовая по улицам на побитых меринах каталась, стреляя из нечищенного ТТ.

Каждый второй блин про мультибайтные кодировки спрашивает, хотя его задача проста до идиотизма - русские буквы на экране видеть. Ну заюзай ты любую из кодировок DOS, не изобретай велосипед. В такое умеет любой вменяемый редактор (ну если вы конечно не в блокноте Windows кодите).

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

я подумал, что IDE умеет, ан нет )))

// �� ������ ������� ������ DetSimen �������� 2020
//
#pragma once

#include <Arduino.h>
#include <SoftwareSerial.h>

const bool ON = true;
const bool OFF = false;

#pragma pack(push,1)

struct FT897DCommand {    // ����� ������

Upper
Offline
Зарегистрирован: 23.06.2020

Voodoo Doll пишет:

Господа, а подскажите почему до сих пор у новичков такие проблемы с UTF-8? Не судьба применить другую кодировку из оперы 437, по типу KOI8 там, не? ...

Я не со стороны "Господ". Дам со своей стороны очевидный (для меня) ответ. У новичка до сих пор проблемы, потому что он каждый раз новый человек.

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

Конкретно в данном случае, логичнее бы звучал вопрос от вас -  "Господа, а подскажите почему до сих пор советуете новичкам использовать UTF-8?". 

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Voodoo Doll пишет:

Господа, а подскажите почему до сих пор у новичков такие проблемы с UTF-8? Не судьба применить другую кодировку из оперы 437, по типу KOI8 там, не? Всё ж придумано до нас ещё на БКшках и спектрумах, когда мы молодого Гейтса с Windows 95 в москвах принимали. Тогда вроде ещё дунди по прилавкам в дефиците лежала, и шваль всякая бритоголовая по улицам на побитых меринах каталась, стреляя из нечищенного ТТ.

Каждый второй блин про мультибайтные кодировки спрашивает, хотя его задача проста до идиотизма - русские буквы на экране видеть. Ну заюзай ты любую из кодировок DOS, не изобретай велосипед. В такое умеет любой вменяемый редактор (ну если вы конечно не в блокноте Windows кодите).

Собственно, здесь имеет место конфликт двух факторов:

1. В байт все существующие в Мире символы не поместить, а потому однобайтовые кодировки - вчерашний день. Не говоря о том, что и с однобайтовыми тоже были сплошные проблемы: разных кодировок кириллицы насчитывалось не менее 6 штук.

2. Компиляторы традиционно требуют однобайтовую кодировку. Собственно, кроме латиницы операторы ЯП ничем другим не пользуются, поэтому одного байта (даже 7 бит) достаточно.

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

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

 

PS. В 437 кодировке кириллицы нет.