Как отрезать определенное количество символов из объекта string?

Тема в разделе "Arduino & Shields", создана пользователем issaom, 11 ноя 2020.

  1. issaom

    issaom Гуру

    Есть строка символов которая содержит произвольную мешанину латиницы/кириллицы

    Код (C++):
    void setup() {
    Serial.begin(115200);
    delay(2000);
    String S = "ZQПЕНЬ";
    String S1;
    S1 = S.substring(0,6);
    Serial.print(S1);
    }
    Вернет ZQПЕ
    А вот:
    Код (C++):

    void setup() {
    Serial.begin(115200);
    delay(2000);
    String S = "ZQwmЕНЬ";
    String S1;
    S1 = S.substring(0,6);
    Serial.print(S1);
    }
    }
    Вернет ZQwmЕ так как кириллица храниться в виде 2-х байт а латиница в виде одного.
    Какой оптимальный алгоритм можно применить, чтобы рассчитать правильно индекс для функции substring ?
    (например чтобы всегда возвращалось только первые 4 символа вне зависимости от того русские эти первые 4 буквы или английские)
     
  2. AlexU

    AlexU Гуру

    В общем случае алгоритм будет довольно-таки сложным.
    Всё зависит от кодировки символов. Если брать простые 8-мибитные кодировки типа ASCII, KOI-8R и т.п., то в этом случае один символ -> один байт.
    А если брать Unicode и кодировки, используемые для него, например, UTF-8, то в этом случае всё печально. Дело в том, что русские символы не обязательно будут двух-байтовыми, хотя в подавляющем большинстве случаев это так. Например символ 'Ё' может быть закодирован как 'Ё' (U-0401), а может как пара символов 'Е' (U-0415) и '..' (U-0308). В последнем случае символ 'Ё' будет занимать четыре байта, но так почти ни когда не делают (ключевое слово -- "почти").

    Но если хотите по-простому вычислять длину строки UTF-8 (именно эта кодировка используется в Arduino IDE), не заморачиваясь с комбинированными символами, то:
    если первый байт соответствует маске 0xxxxxxx, то символ одно-байтный;
    если первый байт соответствует маске 110xxxxx, то символ двух-байтный;
    если первый байт соответствует маске 1110xxxx, то символ трёх-байтный;
    если первый байт соответствует маске 11110xxx, то символ четырёх-байтный.
     
    KindMan, Un_ka, parovoZZ и 4 другим нравится это.
  3. Asper Daffy

    Asper Daffy Иксперд

    Именно так? Ничего другого? Китайщины там? Только латиница и кириллица?

    И ещё вопрос? Тебе это надо в Ардуино IDE или где?
     
  4. issaom

    issaom Гуру

    Символы ASCII и русские буквы - ничего другого нет.
    Да, в Ардуино IDE.
     
  5. Asper Daffy

    Asper Daffy Иксперд

    Ну, вот для затравки, я сделал тебе substring (с одним параметром) как ты просил. Разбирайся.
    Код (C++):
    //
    //    Определение класса "смешанная строка"
    //
    class MixedString : public String {
        static bool isCyrillic(const unsigned char c) { return c == 0xD0 || c == 0xD1; }
    public:
        MixedString(const char *cstr = "") : String(cstr) {}
        MixedString(const String &str) : String(str) {}
        MixedString substring( unsigned int beginIndex ) const;
    };

    MixedString MixedString::substring(unsigned int beginIndex) const {
        unsigned int byteCounter = 0;
        for (uint16_t charCounter = 0; byteCounter < len && charCounter < beginIndex; charCounter++) {
            byteCounter += isCyrillic(buffer[byteCounter]) ? 2 : 1;
        }
        return String::substring(byteCounter);
    }
    //
    //    Конец определения класса "смешанная строка"
    //
    //////////////////////////

    void setup(void) {
        Serial.begin(115200);
        MixedString s = " ЖOПA "; // пробелы и буквы A и O - латинские

        for (int i = 0; i < 5; i++) {
            Serial.print("substring(");
            Serial.print(i);
            Serial.print(")=\"");
            Serial.print(s.substring(i));
            Serial.println("\"");
        }
    }

    void loop(void) {}
    Код (C++):

    substring(0)=" ЖOПA "
    substring(1)="ЖOПA "
    substring(2)="OПA "
    substring(3)="ПA "
    substring(4)="A "

     
    Как видишь, всё работает.

    Но, по уму тут надо доопределить и другие методы, типа charAt, setCharAt, operator [], length, ну и substring с двумя параметрами. Разумеется, также и все конструкторы - их там дохрена. Если разберёшься, как я тот сделал, то и эти сделаешь.
     
    SergeiL и issaom нравится это.
  6. issaom

    issaom Гуру

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

    Код (C++):
    String substringI (String S, uint8_t i) {
      uint8_t j = 0;
      while (i--) {
        j = j + bitRead(S.charAt(j), 7);
        j++;
      }
      return (S.substring(0, j));
    }

    void setup() {
      Serial.begin(115200);
      String SJOPA = "ЖOПA_2020год";
      for (int i = 1; i < 13; i++) {
        Serial.println(substringI(SJOPA,i));
      }
    }

    void loop() {

    }
    Код (C++):
    Ж
    ЖO
    ЖOП
    ЖOПA
    ЖOПA_
    ЖOПA_2
    ЖOПA_20
    ЖOПA_202
    ЖOПA_2020
    ЖOПA_2020г
    ЖOПA_2020го
    ЖOПA_2020год
    Ну вот.... как то так....
     
  7. Asper Daffy

    Asper Daffy Иксперд

    Ну, я вижу, что идею Вы поняли и правильно использовали, теперь напишете что хотите.