из String в char* array

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

есть прототип функции перекодировки для дисплеев

пртотип

char* utf8rus(char *source) {
  uint8_t i = 0;
  unsigned char n;
  uint8_t k = strlen(source);
  char m[2] = {'0', '\0'};
  char* target = (char*)malloc((k + 1) * sizeof(char));
  free(target);
  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; strcat(target, m);
  }
  return target;
}

после переделки получилось два варианта, оба рабочие

1 вариант

char* utf8rus(char *source) {
  uint8_t i = 0;
  unsigned char n;
  uint8_t k = strlen(source);
  char m[2] = {'0', '\0'};
  char* target = (char*)malloc((k + 1) * sizeof(char));
  free(target);
  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; strcat(target, m);
  }
  return target;
}

2 вариант

char* utf8rus(char *source) {
  uint8_t i = 0;
  unsigned char n;
  uint8_t k = strlen(source);
  char* target = (char*)malloc((k + 1) * sizeof(char)), *ptr = target;
  free(target);
  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;
          }
      }
    }
    *ptr++ = n;
  }
  *ptr = 0;
  return target;
}

есть ли ошибки, и какой более лучший?

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

протоип не верный в первом посте, ниже верный

прототип

String utf8rus(String source) {
  uint8_t 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;
}

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

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

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

если не очищать память, то будет переполнение памяти

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

Тут все сплошная ошибка и грабли. 

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

покажите как бы вы сделали

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Если source не нужно сохранять в целости и сохранности, то писать rus прямо в его область, в финале затерминировать и возвратить на него указатель.

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

source мне не нужен после обработки

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

один из вариантов - заранее выделить массив char для результата в основной программе и передать ссылку на него в функцию перекодировки

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

b707 пишет:

один из вариантов - заранее выделить массив char для результата в основной программе и передать ссылку на него в функцию перекодировки

да я об этом думал, но могу забыть потом сделать free();

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Valera19701 пишет:

source мне не нужен после обработки

Ну, вот в фаре накидал, над оптимизацией не думал, в МК не заливал.

char* utf8rus(char* source) {
  byte *ptrRead, *ptrWrite;
  ptrRead = ptrWrite = (byte*) source;
  while (*ptrRead) {
   *ptrWrite = *ptrRead;
   if (*ptrRead >= 0xC0) {
      switch (*ptrRead) {
        case 0xD0: {
            ptrRead++;
            if (*ptrRead == 0x81) {
              *ptrWrite = 0xA8;
              break;
            }
            if (*ptrRead >= 0x90 && *ptrRead <= 0xBF) {
              *ptrWrite = *ptrRead + 0x30;
            }
            break;
          }
        case 0xD1: {
            ptrRead++;
            if (*ptrRead == 0x91) {
              *ptrWrite = 0xB8;
              break;
            }
            if (*ptrRead >= 0x80 && *ptrRead <= 0x8F) {
              *ptrWrite = *ptrRead + 0x70;
            }
            break;
          }
      }
    }
    ptrRead++; ptrWrite++;
  }
  *ptrWrite = 0x00;
  return source;
}

 

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

Valera19701 пишет:

да я об этом думал, но могу забыть потом сделать free();

для глобальных переменных забыть почистить память -  это куда менее опасно, чем для переменных в функции

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

sadman41, проверил ваш код, не работает, в конце слов всякие каракули добавляет

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Запостите от своего краткий рабочий пример, я прогоню на нём.

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

краткий не получится, у меня по 26 слов на двух языках в прогмеме лежит, с возможностью переключения языков на лету

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Ну, что там..

char text[]="UTF-8 here";
utf8rus(text);
Serial.println(text);

Так как-то?

 

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

sadman41, успели забрать

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Valera19701 пишет:
какой более лучший?
Как говаривал И.В. Сталин: "Оба хуже".

А где там, кстати, String? Чёт я его там и не заметил. Его там вообще нет ни в одном из кодов.

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

пост #1

ну так подскажите как лучше сделать

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

Valera19701 пишет:

пост #1

ну так подскажите как лучше сделать

Так уже написали в сообщении №8.

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

andriano, а как лучше, сразу выделить память, или уже внутри функции по размеру слова?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Valera19701, а Вы ответьте какая розетка лучше сетевая или юсб. Вот так и здесь. Если навыков работой с кучей маловато, то просто выделите глобальный буфер. А иначе работайте с кучей оперируя указателями. Ну а если вообще асс, то организуйте класс подобный String. Вот такие пироги с котятами. 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Проблем у себя не вижу


void strAsHex(char* source) {
  char *ptrRead = source;
  while (*ptrRead) {
    Serial.print(" 0x"); Serial.print((byte) *ptrRead, HEX);
    ptrRead++;
  }
  Serial.println();
}

char* utf8rus(char* source) {
  byte *ptrRead, *ptrWrite;
  ptrRead = ptrWrite = (byte*) source;
//  Serial.println(" --- ");
  while (*ptrRead) {
    *ptrWrite = *ptrRead;
//    Serial.print("in: 0x"); Serial.println(*ptrWrite, HEX);
    if (*ptrWrite >= 0xC0) {
      switch (*ptrWrite) {
        case 0xD0: {
            ptrRead++;
//            Serial.print("in: 0x"); Serial.println((byte) *ptrRead, HEX);
            if (*ptrRead == 0x81) {
              *ptrWrite = 0xA8;
              break;
            }
            if (*ptrRead >= 0x90 && *ptrRead <= 0xBF) {
              *ptrWrite = *ptrRead + 0x30;
            }
            break;
          }
        case 0xD1: {
            ptrRead++;
//            Serial.print("in: 0x"); Serial.println((byte) *ptrRead, HEX);
            if (*ptrRead == 0x91) {
              *ptrWrite = 0xB8;
              break;
            }
            if (*ptrRead >= 0x80 && *ptrRead <= 0x8F) {
              *ptrWrite = *ptrRead + 0x70;
            }
            break;
          }
      }
    }
//    Serial.print("out: 0x"); Serial.println((byte) *ptrWrite, HEX);
    ptrRead++; ptrWrite++;
  }
  *ptrWrite = 0x00;
//  Serial.println(" --- ");
  return source;
}

void setup() {
  char text[] = "ПРИВЕТ МИР";
  Serial.begin(115200);
  Serial.print("(1) text: "); Serial.println(text);
  strAsHex(text);
  utf8rus(text);
  Serial.print("(2) text: "); Serial.println(text);
  strAsHex(text);
}

void loop() {}

Вывод:

(1) text: ПРИВЕТ МИР
 0xD0 0x9F 0xD0 0xA0 0xD0 0x98 0xD0 0x92 0xD0 0x95 0xD0 0xA2 0x20 0xD0 0x9C 0xD0 0x98 0xD0 0xA0
(2) text: ⸮⸮⸮⸮⸮⸮ ⸮⸮⸮
 0xCF 0xD0 0xC8 0xC2 0xC5 0xD2 0x20 0xCC 0xC8 0xD0

 

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

sadman41, у вас есть дисплей ssd1306? я вам весь код с библиотекой дам

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

Valera19701 пишет:

sadman41, у вас есть дисплей ssd1306? я вам весь код с библиотекой дам

а зачем оно ему? - вы не верите Садману. что он реально протестировал свой код?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Valera19701 пишет:

andriano, а как лучше, сразу выделить память, или уже внутри функции по размеру слова?

"— Мне водки прям сейчас выпить? Иль уж потом за ужином сразу?" (Мышлаевский):)

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

у меня его код неправильно отображает

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

ЕвгенийП, а по существу?

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

Valera19701 пишет:

andriano, а как лучше, сразу выделить память, или уже внутри функции по размеру слова?

Единственно правильного ответа на Ваш вопрос не существует.

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

 

PS. Ни лично я предпочитаю библиотеки, в которых не нужно ничего перекодировать.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Valera19701 пишет:

sadman41, у вас есть дисплей ssd1306? я вам весь код с библиотекой дам

Ну, где-то валялся, конечно. Но, как я успел увидеть, вы символы в функцию не через выделенный фрагмент памяти передаете, а из прогмемов и как display.print(..., "абвгджз..."); 

В последнем случае, как мне кажется, компилятор временно создаёт анонимный char[], передаёт указатель на него и после выхода из функции сразу уничтожает. Так что тут проблемы на пустом месте возникнуть могут. 

Изначально вы пытались из char[] в char[] перегнать - я вам такую функцию и написал. Она ожидает, что вы сформируете UTF-8 строку в памяти (копированием, например), скормите её перекодировщику и передадите в print(). На иные варианты я не ориентировался.

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

andriano, в том то и вся соль, что есть утечка памяти, поэтому и засунул free() во внутрь функции

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

Valera19701 пишет:

andriano, в том то и вся соль, что есть утечка памяти, поэтому и засунул free() во внутрь функции

странное решение

arduinec
Offline
Зарегистрирован: 01.09.2015

Valera19701 пишет:

есть прототип функции перекодировки для дисплеев

Функция utf8rus(), работающая с char-строками, опубликована в посте #40:
http://arduino.ru/forum/programmirovanie/rusifikatsiya-biblioteki-adafru...

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Valera19701 пишет:

ЕвгенийП, а по существу?

Это и было по существу.

Valera19701 пишет:

в том то и вся соль, что есть утечка памяти, поэтому и засунул free() во внутрь функции

Совершенно секретно. Перед прочтением уничтожить.

(это тоже по существу, если что).

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

а если так ?

char* target = new char();
char* utf8rus(char *source) {
  uint8_t i = 0;
  unsigned char n;
  uint8_t k = strlen(source);
  memset(target, '\0', k + 1);
  char* ptr = target;
  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;
          }
      }
    }
    *ptr++ = n;
  }
  *ptr = 0x00;
  return target;
}

 

nix0id
Offline
Зарегистрирован: 21.11.2017

А в варианте из первого комментария с использованием String в функции будет создан локальный объект, а затем при возврате из функции он будет скопирован (создан новый с тем же значением), а локальный объект будет удален. И утечки памяти не должно быть. Разве нет?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

1.
не вижу вызова этой функции, потому не понимаю как Вы собрались передавать ей параметр.

2.
строка №34 не нужна

3.
в строке №1 запрашивается 1 (один) байт памяти, а в строке №6 в этот самый один байт Вы пытаетесь впихнуть k / 2 + 1 байтов. Значит, при k >= 2 оно туда не поместится. Более того, в строке №32, Вы пытаетесь пихать туда уже k байтов. А запрашивали только один.

4.
Строка №5 допустима только в случае, если в "значимой части" source нет нулей. В Вашем случае это так? Мне что-то кажется. что нули там имеют право быть. Или нет? Если нет, то так можно.

Ну, пока хватит.

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

каким образом он добавляет сам элементы массива?

char* target = new char();
char* utf8rus(char *source) {
  Serial.print(source);
  Serial.print(" : ");
  Serial.print(strlen(target));
  Serial.print(" : ");
  uint8_t i = 0;
  unsigned char n;
  uint8_t k = strlen(source);
  memset(target, '\0', k + 1);
  Serial.print(strlen(target));
  Serial.print(" : ");
  char* ptr = target;
  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;
          }
      }
    }
    *ptr++ = n;
  }
  *ptr = 0x00;
  Serial.println(target);
  return target;
}
void setup() {
  Serial.begin(115200);
  utf8rus("Testing");
  utf8rus("Тест");
}
void loop() {
}

выхлоп

Testing : 0 : 0 : Testing
Тест : 7 : 0 : ⸮⸮⸮⸮

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вы думаете, что от замены

char* target = new char;

на 

char* target = new char();

что-то поменялось? Таки нет. Если нужен массив, то и запрашивайте массив.

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

ничего не поменялось, кроме того что со скобками массив заполнен символами '\0', вопрос был каким образом он меняет свой размер

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Никаким образом не меняет, это иллюзия.

strlen() бежит до первого встреченного '\0'. Таким образом у вас показывается не длина массива (размер выделенного фрагмента памяти), а расстояние от указателя target до ближайшего '\0'. Но так, как эта память ни от кого не защищена, то println() может внезапно показать 9000+ или вообще подвиснуть.

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

просто странно, если обьявляю так target[20]; то больше 20 символов не отображает, а так char* target = new char();  то отображает более 20 символов, вот и озадачился, что за х.....

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Вот интересный вы человек. Поди в каком-нить приёмопередатчике антенну в силовую часть не бросите проводом, а тут пожалста - держите в незастолблённом куске памяти свои данные и удивляетсь.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Valera19701 пишет:

так char* target = new char();  то отображает более 20 символов

При это запрашивает памяти на 1 (ОДИН) символ. А отображает сколько угодно, но из "свободной" памяти, которая может вполне себе оказать и не свободной, а занятой другими переменными. Здравствуйте, глюки!

Вы должны запрашивать столько помяти, сколько нужно.

Пока же, повторяю, Вы запрашиваете один байт, а пихаете туда "сколько придётся".

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

ЕвгенийП, я знаю что вы правы, просто было интересно,  что в таком char target[20]; сразу видно что приплыл, а в таком char* target = new char(20); нет. Кто нибудь так и нарвется :)

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

Valera19701 пишет:

просто было интересно,  что в таком char target[20]; сразу видно что приплыл, а в таком char* target = new char(20); нет. Кто нибудь так и нарвется :)

Валера, похоже, вы этого не понимаете совсем.

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

b707, учиться, учиться, и учиться как сказал великий Ленин :)

arduinec
Offline
Зарегистрирован: 01.09.2015

Valera19701 пишет:
а если так ?

А если так?

#define maxString 21
char target[maxString + 1] = "";

char *utf8rus(char *source)
{
  int i,j,k;
  unsigned char n;
  char m[2] = { '0', '\0' };

  strcpy(target, ""); k = strlen(source); i = j = 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; strcat(target, m);
    j++; if (j >= maxString) break;
  }
  return target;
}
Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

я думаю что лучше так, strcat тормознутая

#define str_utf_len 22
char target[str_utf_len + 1] = "";
char* utf8rus(char *source) {
  uint8_t i = 0, j = 0;
  unsigned char n;
  uint8_t k = strlen(source);
  memset(target, '\0', str_utf_len + 1);
  char* ptr = target;
  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;
          }
      }
    }
    *ptr++ = n; j++; if (j >= str_utf_len) break;
  }
  *ptr = 0x00;
  return target;
}

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

А зачем вы memset/strlen делаете, если strcat избегаете?