Добавить char с интервалами в строку

Я пытаюсь создать функцию, которая будет принимать строку в качестве входных данных и через каждые 20 символов будет вставлять "\n" чтобы строка не уходила за пределы моего экрана (я использую oled с библиотекой SSD1306Ascii).

Пока это мой код, но когда я запускаю его, на экране ничего не появляется (у меня есть другая часть кода, которая передает строку в функцию):

String message = "";
String newMessage = "";

void printMessage(String message) {
  oled.clear();
  if(message.length() > 20) {
    current = 20;
    while(current < message.length()) {
      newMessage = message.substring(current-20, current) + "\n";
      newMessage = newMessage + message.substring(current+1);
      current+20;
    }
    oled.println(newMessage);
  }
  else {
    oled.println(message);
  }
}

, 👍0

Обсуждение

Одна проблема, которая поражает меня, заключается в том, что если длина переданного сообщения составляет 20 или меньше, newMessage останется пустым., @DarioP


2 ответа


Лучший ответ:

0

Я думаю, что ваша реализация в корне ошибочна. В цикле while, когда вы назначаете newMessage элементу message.substring, вы отбрасываете всю работу, проделанную в предыдущих итерациях.

Также я бы рекомендовал разделять разделение и печать. Здесь также очень удобен operator +=. Этот код (непроверенный) должен правильно реализовать вашу первоначальную идею

String splitMessage( const String & message, const unsigned limit) {
  String newMessage = "";
  unsigned i = 0;
  while ( i+limit < message.length() ) {
    newMessage += message.substring(i, i+limit);
    newMessage += '\n';
    i += limit;
  }
  newMessage += message.substring(i);
  return newMessage;
}

Тогда вы можете сделать следующее:

whatever.println(splitMessage(myMessage, 20));

Обратите внимание, что я сохранил использование String из вашей исходной реализации, хотя, находясь во встроенной среде, вероятно, лучше избегать его в пользу C-строк, выделенных в стеке.

Я нашел много полезной информации в различных ответах на этот вопрос: Является ли использование malloc() и free() действительно плохой идеей в Arduino?

,

Объект String создает блок памяти в куче с помощью malloc(), динамически создает объект String newMessage и возвращает объект String из функции, что не является хорошей практикой, поскольку это может привести к утечке памяти в долгосрочной перспективе, потому что память кучи для newMessage никогда не освободится. Для каждого вызова splitMessage() в куче создается новый блок памяти...., @hcheung

@hcheung Я понял тебя. Я программист на С++, все еще знакомлюсь с недостатками «языка» Arduino. Было бы лучше вернуть результат через *неконстантный* ссылочный аргумент? Как void splitMessage (String & out, const String & in, const unsigned limit)?, @DarioP

Как программист на С++, вы должны хорошо знать ловушку malloc() и использования кучи. Строковый объект за сценой использует malloc() для создания переменной, когда вы делаете что-то вроде String newMessage = "";, и каждая итерация конкатенации также вызывает фрагментацию кучи. Общая идея состоит в том, чтобы не использовать String в Arduino (только с 2 КБ ОЗУ), если вы действительно хотите его использовать, в этом конкретном случае я бы объявил его глобальным, чтобы вы могли его потом освободить, но опять же, если вы делаете это таким образом, это не сильно отличается от простого использования массива c вместо объекта String., @hcheung

@hcheung как программист на C ++, я всегда применяю RAII, чтобы выделенная память автоматически освобождалась, когда переменная выходит за пределы области видимости, и меня больше не беспокоит куча., @DarioP


0

Одна проблема как с неудачной реализацией, так и с предыдущим ответом заключается в том, что они выполняют множество динамических распределений и копий. Каждый раз, когда ты используйте операторы String + или +=, которые вы выделяете в куче памяти для новой строки, скопировав исходные данные символов в вновь выделенное пространство и, в конечном итоге, уничтожение исходных строк когда они вам больше не нужны.

Самый простой способ избежать всех этих копий и распределений — не создайте строку, которую вы хотите. Вместо этого просто write() куски один за другим другой на дисплее. Вот нулевое копирование, нулевое динамическое распределение решение:

void printMessage(const char *message) {
    size_t length = strlen(message);
    while (length > 20) {
        oled.write(message, 20);
        oled.write('\n');
        message += 20;  // указываем на остальную часть сообщения
        length  -= 20;  // оставшаяся длина
    }
    oled.write(message);
}

Обратите внимание, что здесь используется метод Print::write(const char *, size_t). чтобы напечатать фрагмент сообщения, даже не создавая его в виде строки в память.

На этих устройствах с ограниченным объемом памяти всегда предпочтительнее избегать выделение кучи и, следовательно, объекты String. Впрочем, если для чего причина, по которой вам действительно нужно напечатать объект String, вы можете использовать следующая перегрузка для печати внутреннего буфера без каких-либо действий дополнительная копия:

void printMessage(const String &message) {
    printMessage(message.c_str());
}
,