Нужно разбить строку на массив

IvanNikolaenko
Offline
Зарегистрирован: 01.02.2018

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

Суть такая: в com порт приходит строка 60;70;59;59;73;75;55;65;67;71

Читаю ее так:

  String myString;
  if (Serial.available() > 0) {
    myString = Serial.readString();
  }
Все получается нормально, но нужно как-то разбить эту строку либо на массив, либо еще как-то. Строка может быть разной длинны
Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015
IvanNikolaenko
Offline
Зарегистрирован: 01.02.2018

Все конечно здорово, но могли бы как то более менее понятней направить?

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

indexOf substring toInt

Ищите ; берете подстроку и преобразуете в число и так далее в цикле пока не конец строки.

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

IvanNikolaenko пишет:

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

Суть такая: в com порт приходит строка 60;70;59;59;73;75;55;65;67;71

Читаю ее так:

  String myString;
  if (Serial.available() > 0) {
    myString = Serial.readString();
  }
Все получается нормально, но нужно как-то разбить эту строку либо на массив, либо еще как-то. Строка может быть разной длинны

вы читаете строку неправильно.

Если в момент, когда вы обратились к Serial, строка еще не принята полностью - у вас в переменной myString будет только начало строки случайного размера, а все остальное либо прочитается в следующий раз, если хватит буфера для хранения, либо будет потеряно.

Дело в том, что оператор Serial.readString() вовсе не ждет, пока строка будет принята полностью. Чтобы код работал правильно, вам надо читать из Serial до тех пор, пока не будет принят маркер окончания строки - обычно символ "\n"

 

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

b707 пишет:

вы читаете строку неправильно.

Оххх :(((

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

b707 пишет:

Дело в том, что оператор Serial.readString() вовсе не ждет, пока строка будет принята полностью. 

и ахх :((

// private method to read stream with timeout
int Stream::timedRead()
{
  int c;
  _startMillis = millis();
  do {
    c = read();
    if (c >= 0) return c;
  } while(millis() - _startMillis < _timeout);
  return -1;     // -1 indicates timeout
} 
//-------------------------------------------------------------------------------
String Stream::readString()
{
  String ret;
  int c = timedRead();
  while (c >= 0)
  {
    ret += (char)c;
    c = timedRead();
  }
  return ret;
} 
//-------------------------------------------------------------------------------
Stream() {_timeout=1000;} 

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

xDriver пишет:

и ахх :((

Чо вздыхаем? Где гарантия-то, что нужная строка будет принята полностью? То, что в исходниках readString для каждого символа таймаут по чтению стоит - не решает ровным счётом ни-че-го. Представьте на секунду, что передающая сторона заткнулась на середине передачи строки "123456", т.е. передала "123" - и всё, встала колом. Принимающая сторона что примет по таймауту? Правильно, строку "123". А что принимающая сторона может, согласно своему алгоритму, ждать? Правильно, "123456". Как сделать вывод, что строка принята не полностью? Правильно, никак, если юзать readString. Патамушта нет никакого признака конца пакета, от слова "совсем".

Ооох, теоретики :)

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

String тут не любят, мож и прально, но я пользуюсь

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


//------------------------------------------------------------------------
uint32_t CountTokens(String str, char separator = ' ') {

  uint32_t count = 0;
  int pos = 0;
  String l_str = str;

  l_str.trim();
  if (l_str.length() <= 0) return 0;
  pos = l_str.indexOf(separator);
  while (pos >= 0) {
    count++;
    pos = l_str.indexOf(separator, pos + 1);
  }
  return ++count;
}
//------------------------------------------------------------------------
String GetToken(String str, uint32_t index, char separator = ' ') {

  uint32_t count = CountTokens(str, separator);

  if (count <= 1 || index < 1 || index > count) return str;

  uint32_t pos_start = 0;
  uint32_t pos_end = str.length();

  count = 0;
  for (uint32_t i = 0; i < pos_end; i++) {
    if (str.charAt(i) == separator) {
      count++;
      if (count == index) {
        pos_end = i;
        break;
      } else {
        pos_start = i + 1;
      }
    }
  }
  return str.substring(pos_start, pos_end);
}

//------------------------------------------------------------------------
void setup() {
  // put your setup code here, to run once:
  String myString = "60;70;59;59;73;75;55;65;67;71";


  Serial.begin(9600);

  Serial.print("String = ");
  Serial.println(myString);

  uint32_t ct = CountTokens(myString, ';');
  Serial.print("Count = ");
  Serial.println(ct, DEC);

  for (uint32_t i = 1; i <= ct; i++) {
    Serial.print("Token ");
    Serial.print(i, DEC);
    Serial.print(" = ");
    Serial.println(GetToken(myString, i, ';'));
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}

 

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

DIYMan пишет:

xDriver пишет:

и ахх :((

Чо вздыхаем? Где гарантия-то, что нужная строка будет принята полностью? То, что в исходниках readString для каждого символа таймаут по чтению стоит - не решает ровным счётом ни-че-го. Представьте на секунду, что передающая сторона заткнулась на середине передачи строки "123456", т.е. передала "123" - и всё, встала колом. Принимающая сторона что примет по таймауту? Правильно, строку "123". А что принимающая сторона может, согласно своему алгоритму, ждать? Правильно, "123456". Как сделать вывод, что строка принята не полностью? Правильно, никак, если юзать readString. Патамушта нет никакого признака конца пакета, от слова "совсем".

Ооох, теоретики :)

Хех! ждал это от b707 :)

Да нет вопросов, без признака окончание строки/пакета может все загнутся и в readString, но как сказал бы ув. b707

"Ну вы других-то совсем за идиотов не держите :)"

в концепции Ардуино readString читать все что пришло/лежит в буфере порта с секундной (по умолчанию) поправкой прилета туда еще чего то. А затыки на передающей стороне, это проблемы передающей стороны, в случае с тем же read можно никогда не дождаться признака окончания строки/пакета и висеть в ожидании вечно.

readString выплюнет то что есть, и это можно уже анализировать :))

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

xDriver пишет:

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

String в функцию по значению? Эээх, не жалко оперативки, от слова "совсем".

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

xDriver пишет:

А затыки на передающей стороне, это проблемы передающей стороны,

Это из серии "все вокруг виноваты, только не мы" :) Пол-страны живёт по таким принципам, однако. Грамотно реализованная принимающая сторона должна разруливать всякие нештатные ситуации, будь то неприём пакета полностью, или - попытка передающей стороны зафлудить канал: в жизни всякое бывает, и если глюк передающей стороны выносит всю сеть устройств на том же RS-485, то нахер нужна и такая передающая сторона, да и для такой принимающей стороны - место тоже в помойке.

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

DIYMan пишет:

xDriver пишет:

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

String в функцию по значению? Эээх, не жалко оперативки, от слова "совсем".

используешь String - про оперативку забудь ! :)

DIYMan пишет:

xDriver пишет:

А затыки на передающей стороне, это проблемы передающей стороны,

Это из серии "все вокруг виноваты, только не мы" :) Пол-страны живёт по таким принципам, однако. Грамотно реализованная принимающая сторона должна разруливать всякие нештатные ситуации, будь то неприём пакета полностью, или - попытка передающей стороны зафлудить канал: в жизни всякое бывает, и если глюк передающей стороны выносит всю сеть устройств на том же RS-485, то нахер нужна и такая передающая сторона, да и для такой принимающей стороны - место тоже в помойке.

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

ну а так и Watchdog на помойку, это удел программеров использующих String и goto :)

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

xDriver пишет:

используешь String - про оперативку забудь ! :)

Ну не по значению же в функции пихать. А так, если про "оперативку забудь", то перерасхода там - 6 байт на объект String. Другое дело, что глобальный объект String будет держать выделенный в оперативке буфер до конца своего существования, поэтому лучше избегать глобальных объектов, ограничиваясь какой-то областью видимости, и всё будет ок. Ну или - создавать объект на куче, чтобы при необходимости всегда можно было убить, высвободив память.

Юзаю String и не парюсь, от слова "совсем", местного неприятия String - не разделяю. Но вот чтоб по значению в функцию передавать - за это самолично готов по рукам бить :) Ибо вот тут как раз - дичайший перерасход оперативки в момент размещения копии объекта на стеке. Представьте, что будет, если объект держит строчку длиной 1200 символов при размере всей оперативки в 2Кб ;) Если передавать по ссылке - таких проблем не возникнет.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

xDriver пишет:

как мне кажется readString и призван чтобы "разруливать всякие нештатные ситуации", 

readString НИ-ЧЕ-ГО не разруливает, ни штатную, ни нештатную ситуацию. Ибо: как вы сможете понять, что это конец пакета? По таймауту? Дык передающая сторона отвалилась на середине передачи. Сама проверка по таймауту означает ровно следующее: если следующего байта нет в приёмном буфере больше, чем секунду - считаем, что мы всё приняли за этот вызов readString, всё. Нет никакой дополнительной семантики у этого метода, никаких нештатных ситуаций он разруливать не способен.

xDriver пишет:

ну а так и Watchdog на помойку, это удел программеров использующих String и goto :)

Это вообще к чему - не понял.

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

DIYMan пишет:

xDriver пишет:

используешь String - про оперативку забудь ! :)

Ну не по значению же в функции пихать. А так, если про "оперативку забудь", то перерасхода там - 6 байт на объект String. Другое дело, что глобальный объект String будет держать выделенный в оперативке буфер до конца своего существования, поэтому лучше избегать глобальных объектов, ограничиваясь какой-то областью видимости, и всё будет ок. Ну или - создавать объект на куче, чтобы при необходимости всегда можно было убить, высвободив память.

Юзаю String и не парюсь, от слова "совсем", местного неприятия String - не разделяю. Но вот чтоб по значению в функцию передавать - за это самолично готов по рукам бить :) Ибо вот тут как раз - дичайший перерасход оперативки в момент размещения копии объекта на стеке. Представьте, что будет, если объект держит строчку длиной 1200 символов при размере всей оперативки в 2Кб ;) Если передавать по ссылке - таких проблем не возникнет.

согласен, принимаю :)

переписал, передаю по ссылке, сэкономил 250 байт на выше приведенном примере, пасиб ! 

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

DIYMan пишет:

Юзаю String и не парюсь, от слова "совсем", местного неприятия String - не разделяю.

Проблема String не в том, что она противопоказана, а в том, что ее применение требует достаточно высокой квалификации программиста (IMHO).

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
{
String i="";
/*некий код*/
} //<--- здесь сработает деструктор и память освободиться
// вот про эту скобку } народ и забывает 

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

andriano пишет:

Проблема String не в том, что она противопоказана, а в том, что ее применение требует достаточно высокой квалификации программиста (IMHO).

Согласен. Из недостатков реализации класса String также отмечу, что метод invalidate(), чистящий динамический буфер, запрятан в секцию protected, поэтому нет никакой возможности ручками почистить оперативку, кроме

String* globalObject = new String();

// bla bla

delete globalObject;
globalObject = new String();

Жутко раздражает, но приходится с этим жить :)

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

qwone пишет:

{
String i="";
/*некий код*/
} //<--- здесь сработает деструктор и память освободиться
// вот про эту скобку } народ и забывает 

 

компилятор подскажет 

'i' was not declared in this scope

или я что то не так понял ?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

xDriver пишет:

qwone пишет:

{
String i="";
/*некий код*/
} //<--- здесь сработает деструктор и память освободиться
// вот про эту скобку } народ и забывает 

 

компилятор подскажет 

'i' was not declared in this scope

или я что то не так понял ?

Нужно окружение, в котором возникает такая ошибка. Вангую, пытаетесь обратиться к i извне блока, ограниченного {}.

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

да я то это понимаю, я у Винни хотел спросить что он имеет ввиду.

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

В нас проросли семена зловредных баобабов. «Если баобаб не распознать вовремя, потом от него уже не избавишься, — предупреждает Маленький Принц. — Он завладеет всей планетой. Он пронижет ее насквозь своими корнями. И если планета очень маленькая, а баобабов много, они разорвут ее на клочки». Вообще, это очень просто — встал поутру, умылся, привел себя в порядок и сразу же приведи в порядок свою планету. Баобабы надо непременно выпалывать каждый день, как только их можно отличить от будущих розовых кустов. Молодые ростки у них почти одинаковые...» https://pikabu.ru/story/quotmalenkiy_printsquot_2033473 

Ну а теперь баобабы заменим на String , а планету за Ардуину. String это переменная переменной длины , причем длина все время увеличивается и забирает память.  Это с одной хорошо, так как данные какими большими они не были влезут в String. Так попользовались Sting и все удалите. 

Возьмем к примеру 

/**/
void setup() {
  Serial.begin(9600);
}
void loop() {
  int i;//<-- локальное i
  i = analogRead(A0);
  Serial.print(i);
}
/*Cкетч использует 1720 байт (5%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 182 байт (8%) динамической памяти, оставляя 1866 байт для локальных переменных. Максимум: 2048 байт.
*/

и этот 

/**/
int i;//<--- глобальная i
void setup() {
  Serial.begin(9600);
}
void loop() {
  i = analogRead(A0);
  Serial.print(i);
}
/*Скетч использует 1728 байт (5%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 184 байт (8%) динамической памяти, оставляя 1864 байт для локальных переменных. Максимум: 2048 байт.
*/

И теперь видно что 2 байта улетело.  И такая история со Sring. И чем локальнее String тем меньше шансов , что он сожрет память.