Очередной парсинг строк. Нид хелп!

3rkv
Offline
Зарегистрирован: 12.02.2021

Всем привет!

Ай нид хелп.

У меня есть входящие команды такого типа:
1. [команда]:[номер]:[номер]
2. [команда]:[номер]:[номер]:[значение]
Мне необходимо их обрабатывать. Сейчас это всё собирается в строку, затем  парсится, раскладывая нужные значения в нужные переменные руками. Меня этот вариант не устраивает.
Мне товарищ сказал что это можно делать быстрее с помощью регулярок типа (\w+):(\d+):(\d+):?(.*) 
Дак, вот, я без понятия как этим пользоваться в дуино.
Если кто имел дело, поделитесь опытом, плиз.
3rkv
Offline
Зарегистрирован: 12.02.2021

вот пример команд:

start:1:1

speed:1:1:1234

time:60

sadman41
Offline
Зарегистрирован: 19.10.2016

Регулярки на AVR? Ну, так себе идея против простого парсинга по разделителям.

3rkv
Offline
Зарегистрирован: 12.02.2021

Вот, тоже, изучил либу подробнее. Она посимвольно перебирает и вообще отжирает много динамической памяти.

Подумываю создать структуру данных содержащую в себе разделитель ":" и несколько переменных, в которых будут храниться полученные данные. Вопрос - насколько это будет грамотно и оптимально для ОЗУ?

Разделитель не типовой, кстати. Причина: дальше это летит в нижний уровень устройства(на СТМ).

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

че там думать то, никакой либы не надо, заводите переменную шага/статуса и в зависимости от входящего символа или дальше ищите число или конец команды

// функция парсинга строки вывода режима и имени оператора после успешного подклбючения к БС
void processingRealOperator(byte stByte) {
  switch (opStepMain) {
    case 0: { // find " ещем кавычку
        if (stByte == '\"') { // если нашлась - начало имени оператора
          memset(opReal, '\0', (max_size_op_short * 2)); // очищаем массив
          opNumStep = 0; ++opStepMain; // обнуляем счетчик длины строки и переходим на след шаг
        }
        break;
      }
    case 1: { // get op name // вытаскиваем имя оператора
        if ((stByte == '\"') || (opNumStep >= ((max_size_op_short * 2) - 1))) { // если найдена кавычка или длина превысила максимум
          ++opStepMain; // заканчиваем обработку
        } else { // иначе
          opReal[opNumStep] = stByte; ++opNumStep; // записываем байт и увеличиваем счетчик байтов - длины строки
        }
        break;
      }
    default: {}
  }
}

// функция парсинга строки качества сигнала
void processingCSQ(byte stByte) {
  if (stByte == ',') { // end str // строка закончилась
    if ((opNumStep > 0) && (opNumStep < 3)) { // если длина строки 1 или 2 байта
      opCSQ = atoi(opTxtCSQ); opNumStep = 6; // преобразуем строку в число и выходим из обработки
    }
  } else if (isdigit(stByte)) { // иначе если пришла цифра
    if (opNumStep < 2) { // если число цифр в строке меньше двух
      opTxtCSQ[opNumStep] = stByte; ++opNumStep; // заносим входящий байт в строку и увеличиваем счетчик длины строки
    }
  }
}

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

вот еще нашел старый пример

void getParseResp(byte inByte) {
  switch (stepParse) {
    case 0: {
        memset(tmpParse, '\0', 5); tmpPos = 0;
        if (isdigit(inByte)) {
          tmpParse[tmpPos] = inByte; ++stepParse; ++tmpPos;
        } else {
          parsePINGresp = false;
        }
        break;
      }
    case 1: {
        if (isdigit(inByte)) {
          tmpParse[tmpPos] = inByte; ++stepParse; ++tmpPos;
        } else {
          if (inByte == '~') {
            byte gpt = atoi(tmpParse);
            if ((gpt >= minResp) && (gpt <= maxResp)) {
              EEPROM.write(pos_eeprom_pingresp, gpt);
              delayWaitMQTTsend = (gpt * 1000UL);
            }
          }
          parsePINGresp = false;
        }
        break;
      }
    case 2: {
        if (isdigit(inByte)) {
          tmpParse[tmpPos] = inByte; ++stepParse; ++tmpPos;
        } else {
          if (inByte == '~') {
            byte gpt = atoi(tmpParse);
            if ((gpt >= minResp) && (gpt <= maxResp)) {
              EEPROM.write(pos_eeprom_pingresp, gpt);
              delayWaitMQTTsend = (gpt * 1000UL);
            }
          }
          parsePINGresp = false;
        }
        break;
      }
    case 3: {
        if (inByte == '~') {
          byte gpt = atoi(tmpParse);
          if ((gpt >= minResp) && (gpt <= maxResp)) {
            EEPROM.write(pos_eeprom_pingresp, gpt);
            delayWaitMQTTsend = (gpt * 1000UL);
          }
        }
        parsePINGresp = false;
        break;
      }
    default: {}
  }
}

 

3rkv
Offline
Зарегистрирован: 12.02.2021

andycat пишет:

че там думать то, никакой либы не надо, заводите переменную шага/статуса и в зависимости от входящего символа или дальше ищите число или конец команды

// функция парсинга строки вывода режима и имени оператора после успешного подклбючения к БС
void processingRealOperator(byte stByte) {
  switch (opStepMain) {
    case 0: { // find " ещем кавычку
        if (stByte == '\"') { // если нашлась - начало имени оператора
          memset(opReal, '\0', (max_size_op_short * 2)); // очищаем массив
          opNumStep = 0; ++opStepMain; // обнуляем счетчик длины строки и переходим на след шаг
        }
        break;
      }
    case 1: { // get op name // вытаскиваем имя оператора
        if ((stByte == '\"') || (opNumStep >= ((max_size_op_short * 2) - 1))) { // если найдена кавычка или длина превысила максимум
          ++opStepMain; // заканчиваем обработку
        } else { // иначе
          opReal[opNumStep] = stByte; ++opNumStep; // записываем байт и увеличиваем счетчик байтов - длины строки
        }
        break;
      }
    default: {}
  }
}

// функция парсинга строки качества сигнала
void processingCSQ(byte stByte) {
  if (stByte == ',') { // end str // строка закончилась
    if ((opNumStep > 0) && (opNumStep < 3)) { // если длина строки 1 или 2 байта
      opCSQ = atoi(opTxtCSQ); opNumStep = 6; // преобразуем строку в число и выходим из обработки
    }
  } else if (isdigit(stByte)) { // иначе если пришла цифра
    if (opNumStep < 2) { // если число цифр в строке меньше двух
      opTxtCSQ[opNumStep] = stByte; ++opNumStep; // заносим входящий байт в строку и увеличиваем счетчик длины строки
    }
  }
}

 

 

Благодарю!

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

3rkv пишет:
с помощью регулярок типа (\w+):(\d+):(\d+):?(.*)

https://github.com/nickgammon/Regexp

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

scanf() заюзать не получица? 

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

3rkv пишет:

вот пример команд:

ua3rkv?

MaksVV
Offline
Зарегистрирован: 06.08.2015

3rkv пишет:
...Сейчас это всё собирается в строку, затем  парсится, раскладывая нужные значения в нужные переменные руками. Меня этот вариант не устраивает.Мне товарищ сказал что это можно делать быстрее

вас не устраивает скорость парсинга при вашем варианте? или код длинный?

можно например так, время парсинга около 100 микросекунд


#define BUF_SIZE 60       // размер буфера парсилки
char currStr[BUF_SIZE+3]; // буфер парсилки  
#define MAX_CHAR_COMMAND 10 // максимальное количество символов в команде
                                  enum {START ,  SPEED,   TIME ,   COMMAND_QUANTITY};   //пишем список команд
const char comm[][MAX_CHAR_COMMAND] = {"start", "speed", "time"}; // массив строк команд (очередность должна совпадать со списком выше)

#define VAR_QUANTITY 3 // здесь задаём максимальное количество переменных после команды
#define DEBUG            // раскоментировать эту строку для отладки в терминал
//#define ZAMER          // раскоментировать эту строку для замера времени парсинга

char *sTr[VAR_QUANTITY+1];   // для парсинга

#ifdef ZAMER
uint32_t t1,t2; // для замера времени, затрачиваемого на парсинг 
#endif
//------------------------------функция исполнения команды
void runCommand(byte command)
{
 int val[VAR_QUANTITY]; // переменные, в которые парсим значения из поступившей строки
#ifdef DEBUG
 Serial.println (comm[command]); //распечатаем поступившую команду в отладку
#endif
 //парсим переменные
 for (int i = 1; i<=VAR_QUANTITY; i++ ) 
 { 
   sTr[i]=strchr(sTr[i-1], ':')+1; 
   val[i-1]= atoi (sTr[i]);
 #if defined DEBUG and not defined ZAMER
   Serial.print ("Val_"); Serial.print (i); Serial.print (": "); Serial.println (val[i-1]);
 #endif
 }
#if defined DEBUG and defined ZAMER
 t2=micros(); // отмечаем время конца парсинга
 Serial.print(t2-t1);  Serial.print (" microseconds");
#endif
      
     if (command==START) {} // делаем нужные действия на соответствующие команды
else if (command==SPEED) {} // делаем нужные действия на соответствующие команды
else if (command==TIME)  {} // делаем нужные действия на соответствующие команды

#ifdef DEBUG
 Serial.println();
#endif
 }

//------------------------функция чтения информации из UART
void UART_read()
{
  if (!Serial.available()) return; // если данных нет игнорируем эту функцию
  char currSymb[2] = {0};          // символьный кэш
  currSymb[0] = Serial.read();     // читаем очередной символ
  static bool stringEnd = 0;       // флаг переполнения буфера

 if (currSymb[0] == '\r' || stringEnd == 1)   // если полностью получили строку - парсим:
 {
  bool commCorrect = 0;   // флаг найдена ли нужная команда
#if defined DEBUG and defined ZAMER
  t1=micros(); // отмечаем время начала парсинга
#endif
  // парсим все команды:
  for (int i=0; i<COMMAND_QUANTITY; i++) {if ((sTr[0] =strstr(currStr, comm[i]))>0) {runCommand (i); commCorrect = 1; break;}}

if (!commCorrect) 
 {
  #ifdef DEBUG
   Serial.println ("Command is not correct!"); // поругаемся, если нет такой команды
  #endif
 }
 
  currStr[0] = 0; stringEnd = 0; // в конце парсинга нулим буфер
 } 

// если не конец строки, то прибавляем очередной байт к буферу : 
else if ( currSymb[0] != '\n')  strcat (currStr,currSymb); 
// если буфер закончился , то режем строку на этом месте: 
     if (strlen(currStr)>=BUF_SIZE) {stringEnd = 1;}

}
//--------------------------------

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

void loop() 
{
UART_read(); // функция чтения из UART
// тут остальной код
}

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Или зависнет нафиг, если в 27ю строку вместо цифры буква прилетит :(

MaksVV
Offline
Зарегистрирован: 06.08.2015

Число в этом случае просто запарсится нулём. из гугла про atoi: 

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

Строка может содержать другие символы после считанного целого числа, эти символы игнорируются и никак не влияют на поведение этой функции.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Да пожалуйста, символ пропустится и хрен с ним, вместо цифры 1234,запишется 234, а впрочем не важно, "хозяин-барин".

MaksVV
Offline
Зарегистрирован: 06.08.2015

 вот во что парсится такая строка  , главное чтобы перед числом не было лишних символов , после можно, пробелы тоже можно

start:  675  da dfa :  4535 bla bla :  7894 ho ho

 start

Val_1: 675
Val_2: 4535
Val_3: 7894
 
MaksVV
Offline
Зарегистрирован: 06.08.2015

andycat пишет:
Да пожалуйста, символ пропустится и хрен с ним, вместо цифры 1234,запишется 234, а впрочем не важно, "хозяин-барин".

ну как бы если такие требования, можно КС какую нибудь вводить в протокол, если символ пропустится пакет вообще-то не валидный

3rkv
Offline
Зарегистрирован: 12.02.2021

MaksVV пишет:

3rkv пишет:
...Сейчас это всё собирается в строку, затем  парсится, раскладывая нужные значения в нужные переменные руками. Меня этот вариант не устраивает.Мне товарищ сказал что это можно делать быстрее

вас не устраивает скорость парсинга при вашем варианте? или код длинный?

можно например так, время парсинга около 100 микросекунд


#define BUF_SIZE 60       // размер буфера парсилки
char currStr[BUF_SIZE+3]; // буфер парсилки  
#define MAX_CHAR_COMMAND 10 // максимальное количество символов в команде
                                  enum {START ,  SPEED,   TIME ,   COMMAND_QUANTITY};   //пишем список команд
const char comm[][MAX_CHAR_COMMAND] = {"start", "speed", "time"}; // массив строк команд (очередность должна совпадать со списком выше)

#define VAR_QUANTITY 3 // здесь задаём максимальное количество переменных после команды
#define DEBUG            // раскоментировать эту строку для отладки в терминал
//#define ZAMER          // раскоментировать эту строку для замера времени парсинга

char *sTr[VAR_QUANTITY+1];   // для парсинга

#ifdef ZAMER
uint32_t t1,t2; // для замера времени, затрачиваемого на парсинг 
#endif
//------------------------------функция исполнения команды
void runCommand(byte command)
{
 int val[VAR_QUANTITY]; // переменные, в которые парсим значения из поступившей строки
#ifdef DEBUG
 Serial.println (comm[command]); //распечатаем поступившую команду в отладку
#endif
 //парсим переменные
 for (int i = 1; i<=VAR_QUANTITY; i++ ) 
 { 
   sTr[i]=strchr(sTr[i-1], ':')+1; 
   val[i-1]= atoi (sTr[i]);
 #if defined DEBUG and not defined ZAMER
   Serial.print ("Val_"); Serial.print (i); Serial.print (": "); Serial.println (val[i-1]);
 #endif
 }
#if defined DEBUG and defined ZAMER
 t2=micros(); // отмечаем время конца парсинга
 Serial.print(t2-t1);  Serial.print (" microseconds");
#endif
      
     if (command==START) {} // делаем нужные действия на соответствующие команды
else if (command==SPEED) {} // делаем нужные действия на соответствующие команды
else if (command==TIME)  {} // делаем нужные действия на соответствующие команды

#ifdef DEBUG
 Serial.println();
#endif
 }

//------------------------функция чтения информации из UART
void UART_read()
{
  if (!Serial.available()) return; // если данных нет игнорируем эту функцию
  char currSymb[2] = {0};          // символьный кэш
  currSymb[0] = Serial.read();     // читаем очередной символ
  static bool stringEnd = 0;       // флаг переполнения буфера

 if (currSymb[0] == '\r' || stringEnd == 1)   // если полностью получили строку - парсим:
 {
  bool commCorrect = 0;   // флаг найдена ли нужная команда
#if defined DEBUG and defined ZAMER
  t1=micros(); // отмечаем время начала парсинга
#endif
  // парсим все команды:
  for (int i=0; i<COMMAND_QUANTITY; i++) {if ((sTr[0] =strstr(currStr, comm[i]))>0) {runCommand (i); commCorrect = 1; break;}}

if (!commCorrect) 
 {
  #ifdef DEBUG
   Serial.println ("Command is not correct!"); // поругаемся, если нет такой команды
  #endif
 }
 
  currStr[0] = 0; stringEnd = 0; // в конце парсинга нулим буфер
 } 

// если не конец строки, то прибавляем очередной байт к буферу : 
else if ( currSymb[0] != '\n')  strcat (currStr,currSymb); 
// если буфер закончился , то режем строку на этом месте: 
     if (strlen(currStr)>=BUF_SIZE) {stringEnd = 1;}

}
//--------------------------------

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

void loop() 
{
UART_read(); // функция чтения из UART
// тут остальной код
}

 

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

3rkv
Offline
Зарегистрирован: 12.02.2021

MaksVV пишет:

3rkv пишет:
...Сейчас это всё собирается в строку, затем  парсится, раскладывая нужные значения в нужные переменные руками. Меня этот вариант не устраивает.Мне товарищ сказал что это можно делать быстрее

вас не устраивает скорость парсинга при вашем варианте? или код длинный?

можно например так, время парсинга около 100 микросекунд


#define BUF_SIZE 60       // размер буфера парсилки
char currStr[BUF_SIZE+3]; // буфер парсилки  
#define MAX_CHAR_COMMAND 10 // максимальное количество символов в команде
                                  enum {START ,  SPEED,   TIME ,   COMMAND_QUANTITY};   //пишем список команд
const char comm[][MAX_CHAR_COMMAND] = {"start", "speed", "time"}; // массив строк команд (очередность должна совпадать со списком выше)

#define VAR_QUANTITY 3 // здесь задаём максимальное количество переменных после команды
#define DEBUG            // раскоментировать эту строку для отладки в терминал
//#define ZAMER          // раскоментировать эту строку для замера времени парсинга

char *sTr[VAR_QUANTITY+1];   // для парсинга

#ifdef ZAMER
uint32_t t1,t2; // для замера времени, затрачиваемого на парсинг 
#endif
//------------------------------функция исполнения команды
void runCommand(byte command)
{
 int val[VAR_QUANTITY]; // переменные, в которые парсим значения из поступившей строки
#ifdef DEBUG
 Serial.println (comm[command]); //распечатаем поступившую команду в отладку
#endif
 //парсим переменные
 for (int i = 1; i<=VAR_QUANTITY; i++ ) 
 { 
   sTr[i]=strchr(sTr[i-1], ':')+1; 
   val[i-1]= atoi (sTr[i]);
 #if defined DEBUG and not defined ZAMER
   Serial.print ("Val_"); Serial.print (i); Serial.print (": "); Serial.println (val[i-1]);
 #endif
 }
#if defined DEBUG and defined ZAMER
 t2=micros(); // отмечаем время конца парсинга
 Serial.print(t2-t1);  Serial.print (" microseconds");
#endif
      
     if (command==START) {} // делаем нужные действия на соответствующие команды
else if (command==SPEED) {} // делаем нужные действия на соответствующие команды
else if (command==TIME)  {} // делаем нужные действия на соответствующие команды

#ifdef DEBUG
 Serial.println();
#endif
 }

//------------------------функция чтения информации из UART
void UART_read()
{
  if (!Serial.available()) return; // если данных нет игнорируем эту функцию
  char currSymb[2] = {0};          // символьный кэш
  currSymb[0] = Serial.read();     // читаем очередной символ
  static bool stringEnd = 0;       // флаг переполнения буфера

 if (currSymb[0] == '\r' || stringEnd == 1)   // если полностью получили строку - парсим:
 {
  bool commCorrect = 0;   // флаг найдена ли нужная команда
#if defined DEBUG and defined ZAMER
  t1=micros(); // отмечаем время начала парсинга
#endif
  // парсим все команды:
  for (int i=0; i<COMMAND_QUANTITY; i++) {if ((sTr[0] =strstr(currStr, comm[i]))>0) {runCommand (i); commCorrect = 1; break;}}

if (!commCorrect) 
 {
  #ifdef DEBUG
   Serial.println ("Command is not correct!"); // поругаемся, если нет такой команды
  #endif
 }
 
  currStr[0] = 0; stringEnd = 0; // в конце парсинга нулим буфер
 } 

// если не конец строки, то прибавляем очередной байт к буферу : 
else if ( currSymb[0] != '\n')  strcat (currStr,currSymb); 
// если буфер закончился , то режем строку на этом месте: 
     if (strlen(currStr)>=BUF_SIZE) {stringEnd = 1;}

}
//--------------------------------

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

void loop() 
{
UART_read(); // функция чтения из UART
// тут остальной код
}

 

 

как - то так по итогу и вышло. Только я одного понять не могу - все всегда парсят посимвольно. Вопрос зачем, если можно так IncomingDataBT = Serial3.readStringUntil('\n');?

3rkv
Offline
Зарегистрирован: 12.02.2021
unsigned long parce_i_data(String inc_data)
{
  switch (In_cmd.Step)
  {
    case 1:
      {
        In_cmd.idx = inc_data.indexOf(In_cmd.separator);
        In_cmd.command = inc_data.substring(0, In_cmd.idx);
//        Serial.println("Command:" + In_cmd.command);
        ++In_cmd.Step;
        ++In_cmd.idx;
      } 
    case 2:
      {
        if (inc_data.indexOf(In_cmd.separator, In_cmd.idx) != -1)
        {
          In_cmd.mS_1 = inc_data.substring(In_cmd.idx, inc_data.indexOf(In_cmd.separator, In_cmd.idx));
          In_cmd.value = 0;
          ++In_cmd.idx;
          ++In_cmd.Step;
//          Serial.println("Motor 1 state =" + (String)In_cmd.mS_1);
        }
        else
        {
          char long_buf[256];
          (inc_data.substring(In_cmd.idx)).toCharArray(long_buf,256);
          In_cmd.value = atol(long_buf);
          In_cmd.Step = 1;
//          Serial.println("Value =" + (String)In_cmd.value);
          In_cmd.mS_1 = "0";
          In_cmd.mS_2 = "0";
          break;
        }
      }
    case 3:
      {
        if (inc_data.indexOf(In_cmd.separator, In_cmd.idx) != -1)
        {
          ++In_cmd.idx;
          In_cmd.mS_2 = inc_data.substring(In_cmd.idx, In_cmd.idx + 1);
//          Serial.println("Motor 2 state =" + (String)In_cmd.mS_2);
          ++In_cmd.idx;
          ++In_cmd.Step;
        }
        else {
          In_cmd.Step = 1;
          break;
        }
      }
    case 4:
      {
        if (inc_data.indexOf(In_cmd.separator, In_cmd.idx) != -1)
        {
          ++In_cmd.idx;
          In_cmd.value = (inc_data.substring(In_cmd.idx)).toInt();
//          Serial.println("Value =" + (String)In_cmd.value);
          In_cmd.Step = 1;
          break;
        }
        else
        {
          In_cmd.Step = 1;
          break;
        }
      }
  }

как-то так в итоге

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

3rkv пишет:

 Только я одного понять не могу - все всегда парсят посимвольно. Вопрос зачем, если можно так IncomingDataBT = Serial3.readStringUntil('\n');?

вы удивитесь, но посимвольно быстрее

sadman41
Offline
Зарегистрирован: 19.10.2016

Я бы не сказал "быстрее", скорее "многозадачней".

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

sadman41 пишет:
Я бы не сказал "быстрее", скорее "многозадачней".

И это тоже. А еще более гибко - разбираемая строка не обязательно должна иметь  перевод в конце.

А при отсутствии "\n" в конце строки - будет еще и быстрее :))

Kakmyc
Offline
Зарегистрирован: 15.01.2018

3rkv пишет:

как - то так по итогу и вышло. Только я одного понять не могу - все всегда парсят посимвольно. Вопрос зачем, если можно так IncomingDataBT = Serial3.readStringUntil('\n');?

А что будет , если в момент перевода строки данные потеряются ?

Нужно рассматривать работу системы не только в сферическом вакууме

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

3rkv пишет:

Только я одного понять не могу - все всегда парсят посимвольно. Вопрос зачем, если можно так IncomingDataBT = Serial3.readStringUntil('\n');?

Потому что "так" - можно не всегда, а только для относительно коротких строк. Есть и другие причины. В общем, во всех отношениях парсить посимвольно - лучше.

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

3rkv пишет:
с помощью регулярок типа (\w+):(\d+):(\d+):?(.*)
Так Вы пробовали библиотеку, что я Вам в #7 дал? Или нафиг?

MaksVV
Offline
Зарегистрирован: 06.08.2015

3rkv пишет:
как - то так по итогу и вышло. Только я одного понять не могу - все всегда парсят посимвольно. Вопрос зачем, если можно так IncomingDataBT = Serial3.readStringUntil('\n');?

Потомучто

3rkv
Offline
Зарегистрирован: 12.02.2021

Kakmyc пишет:
3rkv пишет:

как - то так по итогу и вышло. Только я одного понять не могу - все всегда парсят посимвольно. Вопрос зачем, если можно так IncomingDataBT = Serial3.readStringUntil('\n');?

А что будет , если в момент перевода строки данные потеряются ? Нужно рассматривать работу системы не только в сферическом вакууме

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

3rkv
Offline
Зарегистрирован: 12.02.2021

-

3rkv
Offline
Зарегистрирован: 12.02.2021

ЕвгенийП пишет:

3rkv пишет:
с помощью регулярок типа (\w+):(\d+):(\d+):?(.*)
Так Вы пробовали библиотеку, что я Вам в #7 дал? Или нафиг?

Я скачивал её ещё ранее. Поковырял, посмотрел. Меня не устроила по двум причинам:

1.Слишком много жрёт для такого парсинга(Честно говоря я бы подумал несколько раз чтобы пихать такое в дуино).

2.Разбирает по факту так же посимвольно. Очень костыльно выходит.

3rkv
Offline
Зарегистрирован: 12.02.2021

MaksVV пишет:

3rkv пишет:
как - то так по итогу и вышло. Только я одного понять не могу - все всегда парсят посимвольно. Вопрос зачем, если можно так IncomingDataBT = Serial3.readStringUntil('\n');?

Потомучто

Ну, два момента:

1. Я не использую луп вообще.

2. Если не пришел символ конца строки, значит канал данных не стабилен и использование такого канала без протоколов(например HDLC) рисковано.

Другими словами, если у Вас не пришел конец строки, который должен прийти, что в первом, что во втором случае является ошибкой в коммуникации устройств.

Что даёт посимвольный парсинг, если вы в любом случае не получаете нужного терминатора строки?

Кроме того, обратите внимание, что в посте речь идёт о посимвольном считывании из серийного порта с использованием не явного приведения типов.

Ваше "потомучто" не объяснило ничего.

3rkv
Offline
Зарегистрирован: 12.02.2021

Вот немного мат.части. Давайте рассмотрим вопрос, опираясь на офф документацию.

1. Задержку выполнения кода вызванную отсутствием терминируещего символа можно регулировать.

2. Сама по себе функция посимвольно складывает данные из буфера в строку, т.е. в массив символов.

Я вижу это так:

Выполняем проверку при парсинге в два уровня.

Первый - проверяем целостность строки по терминирующему символу

Второй - уже во время парсинга смотрим наличие разделителей.

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

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

3rkv пишет:

Вот немного мат.части

Простите, можно уточнить Вашу цель при создании этой темы?

В заголовке Вы написали "Нид хелп!", а на деле занимаетесь тем, что учите "неправильно парсить строки" тех, кто пытается Вам помочь.

Если Вы лучше знаете как надо делать и разбираетесь в матчасти, так и делайте на здоровье. Какого ещё хелпа Вам надо?

3rkv
Offline
Зарегистрирован: 12.02.2021

Да, я, собственно, уже получил помощь. Просто, обсуждение продолжается. А мне любопытно.

На деле я не учу никого парсить строки. Идет рассуждение на тему того как лучше это делать.

Мне говорят - "вот так", я спрашиваю "почему". Далее привожу довод почему меня это смущает и предлагаю всем вместе разобраться в теме.

Я человек дотошный и принять "потому что" на веру мне сложно, как и сложно принять "потому что так все делают". Я уверен, что тут есть спецы с огромным опытом в программировании дуино, которые смогут мне объяснить, если захотят конечно. 

Если у Вас такого желания нет, то просто пройдите мимо.

 

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

3rkv пишет:
На деле я не учу никого

Да, Вы просто указываете, что мне делать

3rkv пишет:
просто пройдите мимо.

А почему собственно я должен проходить мимо, если, как Вы изволили выразиться,

3rkv пишет:
мне любопытно

Я задал Вам вопрос, а Вы меня лесом ... Вам одному позволительно задавать вопросы? Или как?

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

По моему тут идет смешение понятий "получение данных" и "парсинг данных."

Получать часто лучше (в плане возможной "многозадачности") посимвольно. А собственно парсинг - в зависимости от задачи, и тут (по моему) нет универсального совета.

3rkv
Offline
Зарегистрирован: 12.02.2021

ЕвгенийП пишет:

3rkv пишет:
На деле я не учу никого

Да, Вы просто указываете, что мне делать

3rkv пишет:
просто пройдите мимо.

А почему собственно я должен проходить мимо, если, как Вы изволили выразиться,

3rkv пишет:
мне любопытно

Я задал Вам вопрос, а Вы меня лесом ... Вам одному позволительно задавать вопросы? Или как?

Тут проблема в Вашем восприятии.

А ещё скиньте пожалуйста цитатой, где я лично Вам указываю что делать. Так сказать "пруфы где")

3rkv
Offline
Зарегистрирован: 12.02.2021

Думаю Вы правы, учитывая, что бывают "сырые" данные. А что касается смешения - тут невероятно тонкая грань.

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

3rkv пишет:

2. Сама по себе функция посимвольно складывает данные из буфера в строку, т.е. в массив символов.

ну вот, насчета приема  вы уже согласились - прием в любом случае выполняется посимвольно.

Остается парсинг. Если не упираться в пустой спор, а немного подумать - внутри себя все функции. которые якобы "работают со строкой" - на самом деле разбирают ее посимвольно . Просто это скрыто от вас "под капотом".

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

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

3rkv пишет:

Вот немного мат.части. Давайте рассмотрим вопрос, опираясь на офф документацию.

1. Задержку выполнения кода вызванную отсутствием терминируещего символа можно регулировать.

2. Сама по себе функция посимвольно складывает данные из буфера в строку, т.е. в массив символов.

Я вижу это так:

Выполняем проверку при парсинге в два уровня.

Первый - проверяем целостность строки по терминирующему символу

Второй - уже во время парсинга смотрим наличие разделителей.

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

По утрам я всегда добрый, пока еще трезвый ;)). Поэтому поясню, но ровно до первого спора. Ни с Евгением, ни со мной ни с кем из старых участников у тебя еще "орган не отрос" спорить. Пока на таких условиях.

Отвечать буду по ходу цитаты, без нумерования. Цитата - твоя, разберешься. Это ж тебе надо, так-то, по-большому? Речь будет про 8-ми битные AVR контроллеры. На ESP - делай как тебе нравится, там и частота и память совсем в иных количествах

Если что-то будет нуждаться в дополнительных пояснениях - ссылайся уже на мой номер.

1. Использовать readStringUntil() - можно очень хорошо понимая где и как ты это делаешь. Она ждет терминатора в течении заданного таймаута, по умолчанию - 1 сек. На это время работа контроллера - остановлена, кроме прерываний.  Здесь понял?

2. Регулировать можно, если она маленькая - то вообще не имеет смысла. Строка не пришла, что ты делаешь? Она-то пришла, просто ты неудачно попал вне таймаута.

3. Сперва ты принимаешь строку, реально - по символу, или, вместо приема по символу, провисишь в ожидании, пока строка придет со скоростью... хорошо, если 115200, а если 9600? ;)) А потом посимвольно парсишь. Почему "посимвольно"? - потому, что у процессора одно ядро и параллелить он не умеет, как бы ты себя не обманывал - обработка идет посимвольно. Другое дело, что на больших компьютерах или контроллерах есть стандартные библиотеки, которые спрячут от тебя обработку и тебе, как программисту, станет легче писать код.

4. как и написано выше - ты сам признаешься, что тебе "удобнее" работать со строкой. Не дай Б..г, еще и тип String в AVR 8-и битный потянешь! Это понятно - приход в контроллеры, или, как говорят программисты "в ембеддинг" из, прости Господи, веба и чего-то подобного! ;)) Да, я немного глумлюсь, прости. Ембеддеры - в неком смысле "элита" программирования, а веб-кодеры - на уровне технического персонала. Не нужно с этим спорить, это не истина в последней инстанции, а распространенное мнение. Просто имей ввиду, как смотрят тут на "веб-кодеров".

5. Ладно, это было отвлечение в сторону. Теперь к делу. Еще раз: представь себе маленький контроллер и прием команды по сериалу со скоростью 9600. Символ целиком придет за 1 мс. Это 16000 тактов процессора. И ты все это время будешь просто курить бамбук? А зачем? Если потом ты все равно будешь парсить строку. И, если не применять библиотеки типа той, что указал Женя, то парсить посимвольно. Ну так и сделай за эти 16000 тактов что-то полезное, в частности порцию парсинга. И не забываем, что оперативки у нас 2К. Так что лишний буфер на строку - это не "крохи".

6. Вообще, правильный стиль в парадигме "сетап-луп", используемой в Ардуино, это принять ОДИН символ в проходе лупа и сделать необходимый анализ его для парсинга. Таким образом нет простоя контроллера и выполняются иные полезные действия, для которых прибор проектируется - управление внешними устройствами и сбор данных с датчиков. Многие новички делают ту самую ошибку, к которой ты стремишься ;)) - принять в одном проходе лупа больше одного символа? Подумай - зачем? Проход лупа - как автобус - всегда будет следующий. ;))

Вообще, при обучении, правильный подход - копирование лучших техник, а потом уже, с опытом придет понимание - где и как можно "срезать".

Когда-то в юности меня учил сваривать оптоволокно старый дядька, советской выучки, еще на сварочнике без экрана, со стерео-микроскопом. Он говорил: "Делай хорошо, а плохо - оно само получится!". Хорошая мысль, нет?

3rkv
Offline
Зарегистрирован: 12.02.2021

Мне не понятно в чём заключается моё учение кого либо? Что за голословные фразы?

Подкрепите, пожалуйста, их цитатами.

Если такие найдутся, я сменю форму подачи информации в массы. Я искренне не понимаю этих обвинений.

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

3rkv пишет:

Если такие найдутся, я сменю форму подачи информации в массы. Я искренне не понимаю этих обвинений.

Положим не "учите", а беретесь "подавать информацию в массы" - не разобравшись сами, что же эта информация значит.

 

3rkv
Offline
Зарегистрирован: 12.02.2021

Вы извините, но я не статью на вики пишу.

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

3rkv пишет:

Вы извините, но я не статью на вики пишу.

по-моему, уже начались "виляния хвостом".

Все эти обсуждения кто кого учит, как надо подавать информацию - не более чем попытки уйти от обсуждения сути "построчного парсинга", поскольку ТС начинает понимать, что в сути вопроса он явно сел в лужу...

3rkv
Offline
Зарегистрирован: 12.02.2021

Спасибо тебе ВечноМолодойИПокаЕщёТрезвый!

Вот, как раз с вебом-то я и не знаком) Оттуда и вопрос появился про регулярки(слышал звон да не знаю... Вот и пошёл спрашивать у знающих).

3rkv
Offline
Зарегистрирован: 12.02.2021

Это не "виляния хвостов", я цитаты не увидел. Видимо нечего было скидывать, потому "объезд" темы.

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

3rkv пишет:

А ещё скиньте пожалуйста цитатой, где я лично Вам указываю что делать. Так сказать "пруфы где")

Вы не умеете читать?

Или вот эта фраза в посте, обращённом лично ко мне, не есть указание, что мне делать?

3rkv пишет:
просто пройдите мимо.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

3rkv пишет:

Мне не понятно в чём заключается моё учение кого либо? Что за голословные фразы?

Подкрепите, пожалуйста, их цитатами.

Если такие найдутся, я сменю форму подачи информации в массы. Я искренне не понимаю этих обвинений.

Цитата "вот  немного мат.части". "давайте рассмотрим" и подобное.

Родное сердце, выше уже в иных фразах спрашивали тебя: Ты пришел узнать что-то или подискутировать от избытка времени? ;))

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

Спросить - тебе объяснили, и я - в том числе, ПОЧЕМУ в контроллере правильно парсить по мере прихода символов из сериала. Если ты, обладая пытливостью ума, хочешь что-то уточнить - велком! Я поясню.

Если ты настаиваешь на том, чтобы сперва принять всю строку, а потом парсить - пиши так, только доказывать эту точку зрения не надо, она - неправильная. И тебе уже объяснили - почему. Тогда следует признать, что "отношения не сложились" и не стоит продолжать бессмысленное общение.... на срач скатится.

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

wdrakula пишет:

Если ты настаиваешь на том, чтобы сперва принять всю строку, а потом парсить - пиши так, только доказывать эту точку зрения не надо, она - неправильная. 

Чойта?  Я всегда сначала принимаю всю строку, дабы удебицца, что она не битая, а потом отдаю на парсинг. Строка в текстовом виде, в формате: 

L#cc...cc#CRC

L - длина строки унутре #..#, 1-3 символа

# - маркер начала и конца значимых данных

ccc...ccc -  сопсно, значимые данные

СRC - Контрольная сумма crc16 1-5 символов 

3rkv
Offline
Зарегистрирован: 12.02.2021

Благодарю за объяснение, всех принимавших участие и проявивших благоразумие не отвлекаться на мелочи.

Евгений П, серьезно? расслабьтесь(советую, не указываю), словно тем больше нет для обсуждений, кроме как за слова цепляться. Если так трогает моя ошибка в подаче текста Вас и кого то ещё, извиняюсь. Нет, я не виляю(вдруг кому показалось), свожу бессмысленные споры на нет.

Есть косяк - задел человека(может и не одного), извинился. 

А что касается созданной мной темы, то я, в общем-то, узнал всё что было интересно и даже более.

Считаю тему закрытой.

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

DetSimen пишет:

wdrakula пишет:

Если ты настаиваешь на том, чтобы сперва принять всю строку, а потом парсить - пиши так, только доказывать эту точку зрения не надо, она - неправильная. 

Чойта?  Я всегда сначала принимаю всю строку, дабы удебицца, что она не битая, а потом отдаю на парсинг. Строка в текстовом виде, в формате: 

L#cc...cc#CRC

L - длина строки унутре #..#, 1-3 символа

# - маркер начала и конца значимых данных

ccc...ccc -  сопсно, значимые данные

СRC - Контрольная сумма crc16 1-5 символов 

Не уверен, что даже в твоем исполнении это разумно, но твой уровень знаний позволяет делать так, как тебе хочется. Что нельзя сказать о ТС. Ты же не висишь в readStringUntil() ? Воот! Об этом и речь!

incdpr
Offline
Зарегистрирован: 28.11.2020

Ну вот у меня про что-то подобное. 

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

У меня данные приходят из блютуса.

Так понимаю они приходят посимвольно. Проблема в том, что надо отследить, что они все пришли. Для этого все char -ы посимвольно заталкиваем в строку, "отлавливаем" последний какой-то там нулевой символ типа '\n'. И потом парсим строку (по парсингу я задам отдельный вопрос) по какому то символу, например по ':' и расталкиваем по другим массивам и строкам.

Это если без библиотек.

Ну ладно. А не может в таком случае прийти не все символы, но в середине.

Т.е. должно прийти например 'H' 'e' 'l' 'l' 'o' '\n', а придёт 'H' 'e' 'l' 'o' '\n'. Вроде и есть конец символа строки, но строка не та, какая нужна. Может есть для таких случаев какая-нибудь "Хеш сумма"?

И 2 вариант, прямо с ходу считывать символы, расталкивая (дойдя до ':') по массивам и строкам, а если в конце пришёл '\n' т.е. bool OK = true и работать дальше с новыми массивами и строками?

Какой вариант использовать лучше 1-й или 2-й? 

Я хочу делать по 1-му, но кажется, что и 1-й вариант не исключает ошибки