Как разбить входящую строку?

Я отправляю список позиций сервоприводов через последовательное соединение с arduino в следующем формате

1: 90 & Amp; 2: 80 & амп; 3: 180

, который будет анализироваться как:

servoId: Position & servoId: Position & servoId: Position

Как бы разбить эти значения и преобразовать их в целое число?

43 голоса | спросил ValrikRobot 31 MaramMon, 31 Mar 2014 10:35:44 +04002014-03-31T10:35:44+04:0010 2014, 10:35:44

10 ответов


63

В противоположность другим ответам, я предпочел бы держаться подальше от String по следующим причинам:

  • использование динамической памяти (что может быстро привести к фрагментации кучи и исчерпанию памяти )
  • довольно медленно из-за операторов построения /уничтожения /назначения

Во встроенной среде, такой как Arduino (даже для Mega с большим количеством SRAM), я бы предпочел использовать стандартные функции C :

  • strchr () : поиск символа в строке C (т.е. char *)
  • strtok () : разбивает строку C на подстроки на основе символа разделителя
  • atoi () : преобразует строку C в код int

Это приведет к следующему образцу кода:

 //Рассчитать на основе максимального размера ввода, ожидаемого для одной команды
#define INPUT_SIZE 30
...

//Получить следующую команду из Serial (добавить 1 для окончательного 0)
char input [INPUT_SIZE + 1];
размер байта = Serial.readBytes (вход, INPUT_SIZE);
//Добавить окончательный 0 для завершения строки C
input [size] = 0;

//Прочитайте каждую пару команд
char * command = strtok (input, "&");
while (command! = 0)
{
    //Разделить команду на два значения
    char * separator = strchr (команда, ':');
    if (разделитель! = 0)
    {
        //Собственно разделим строку на 2: заменим ':' на 0
        * separator = 0;
        int servoId = atoi (команда);
        ++ сепаратор;
        int position = atoi (разделитель);

        //Делаем что-то с сервоприводом и позицией
    }
    //Найти следующую команду в строке ввода
    command = strtok (0, "&");
}

Преимущество в том, что динамическое распределение памяти не происходит; вы можете даже объявить input как локальную переменную внутри функции, которая будет читать команды и выполнять их; как только функция возвращается, размер, занимаемый input (в стеке), восстанавливается.

ответил jfpoilpret 31 MarpmMon, 31 Mar 2014 22:12:50 +04002014-03-31T22:12:50+04:0010 2014, 22:12:50
15

Эта функция может использоваться для разделения строки на части на основе разделительного символа.

Строка xval = getValue (myString, ':', 0);
String yval = getValue (myString, ':', 1);

Serial.println ("Y:" + yval);
Serial.print ("X:" + xval);

Преобразовать строку в int

int xvalue = stringToNumber (xval);
int yvalue = stringToNumber (yval);

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

String getValue (данные String, разделитель символов, индекс int)
{
    int found = 0;
    int strIndex [] = {0, -1};
    int maxIndex = data.length () - 1;

    для (int i = 0; i <= maxIndex & <= lt; = index; i ++) {
        if (data.charAt (i) == separator || i == maxIndex) {
            найдено ++;
            strIndex [0] = strIndex [1] + 1;
            strIndex [1] = (i == maxIndex)? i + 1: i;
        }
    }
    return found> индекс ? data.substring (strIndex [0], strIndex [1]): "";
}
ответил oharkins 19 PMpSat, 19 Apr 2014 23:08:33 +040008Saturday 2014, 23:08:33
11

Вы можете сделать что-то вроде следующего, но, пожалуйста, учтите несколько вещей:

Если вы используете readStringUntil (), он будет ждать, пока он не получит символ или таймауты. Таким образом, с вашей текущей строкой последняя позиция будет длиться немного дольше, так как она должна ждать. Вы можете добавить конечный &, чтобы избежать этого тайм-аута. Вы можете легко проверить это поведение на своем мониторе, попробуйте отправить строку с дополнительным & и вы увидите такую ​​задержку ожидания.

Вам действительно не нужен индекс сервомашинки, вы можете просто отправить свою строку позиций и получить индекс сервопривода по позиции значения в строке, например: 90 & 80 & 180 & , Если вы используете индекс сервопривода, возможно, вы хотите его проверить (преобразовать в int), а затем сопоставить индекс цикла i), чтобы убедиться, что с вашим сообщением ничего не получилось.

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

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

void loop () {
    для (int i = 1; i <= 3; i ++) {
        String servo = Serial.readStringUntil (':');
        if (servo! = "") {
            //здесь вы можете проверить номер сервопривода
            String pos = Serial.readStringUntil ('&');
            int int_pos = pos.toInt ();
            Serial.println ( "Позы");
            Serial.println (int_pos);
        }
    }
}
ответил drodri 31 MaramMon, 31 Mar 2014 11:51:40 +04002014-03-31T11:51:40+04:0011 2014, 11:51:40
6

Вы можете использовать Stream.readStringUntil (terminator) прохождение другого терминатора для каждой части .

В каждой части вы вызываете String.toInt

ответил Federico Fissore 31 MaramMon, 31 Mar 2014 11:36:29 +04002014-03-31T11:36:29+04:0011 2014, 11:36:29
4

См. пример: https://github.com/BenTommyE/Arduino_getStringPartByNr

//разделение строки и возвращение раздела nr index split разделителем
String getStringPartByNr (данные String, разделитель символов, индекс int) {
    int stringData = 0; //переменная для подсчета части данных nr
    String dataPart = ""; //переменная для удаления возвращаемого текста

    for (int i = 0; i <data.length () - 1; i ++) {//Прогулка по тексту по одной букве за раз
        if (data [i] == separator) {
            //Подсчитайте количество символов разделителя в тексте
            StringData ++;
        } else if (stringData == index) {
            //получить текст, когда разделитель является rignt one
            dataPart.concat (данные [I]);
        } else if (stringData> index) {
            //возвращаем текст и останавливаем, если появляется следующий разделитель - чтобы сохранить время CPU
            return dataPart;
            ломать;
        }
    }
    //вернуть текст, если это последняя часть
    return dataPart;
}
ответил Ben-Tommy Eriksen 14 MarpmSat, 14 Mar 2015 21:58:17 +03002015-03-14T21:58:17+03:0009 2015, 21:58:17
3
String getValue (данные String, разделитель символов, индекс int)
{
    int maxIndex = data.length () - 1;
    int j = 0;
    Строка chunkVal = "";

    для (int i = 0; i <= maxIndex & j <= index; i ++)
    {
        chunkVal.concat (данные [I]);

        if (data [i] == separator)
        {
            j ++;

            если (j> индекс)
            {
                chunkVal.trim ();
                return chunkVal;
            }

            chunkVal = "";
        }
        else if ((i = maxIndex) & & (j <index)) {
            chunkVal = "";
            return chunkVal;
        }
    }
}
ответил Jam Ville 9 MarpmMon, 09 Mar 2015 20:07:25 +03002015-03-09T20:07:25+03:0008 2015, 20:07:25
2

Самое простое решение - использовать sscanf () .

int id1, id2, id3;
  int pos1, pos2, pos3;
  char * buf = "1: 90 & 2: 80 & 3: 180";
  int n = sscanf (buf, "% d:% d &% d:% d &% d:% d", & id1, & pos1, & id2, & pos2, & id3, & pos3 );
  Serial.print (Р ( "п ="));
  Serial.println (п);
  Serial.print (Р ( "ID1 ="));
  Serial.print (ID1);
  Serial.print (F (", pos1 ="));
  Serial.println (Pos1);
  Serial.print (Р ( "ID2 ="));
  Serial.print (ID2);
  Serial.print (F (", pos2 ="));
  Serial.println (pos2);
  Serial.print (Р ( "id3 ="));
  Serial.print (id3);
  Serial.print (F (", pos3 ="));
  Serial.println (POS3);

Это даст следующий результат:

п = 6
id1 = 1, pos1 = 90
id2 = 2, pos2 = 80
id3 = 3, pos3 = 180

Ура!

ответил Mikael Patel 18 FebruaryEurope/MoscowbThu, 18 Feb 2016 17:18:54 +0300000000pmThu, 18 Feb 2016 17:18:54 +030016 2016, 17:18:54
1
char str [] = "1: 90 & 2: 80 & 3: 180"; //тестовый пример последовательного ввода из сервопривода
int servoId;
int позиция;

char * p = str;
while (sscanf (p, "% d:% d", & servoId, & position) == 2)
{
    //обрабатываем servoId, позицию здесь
    //
    while (* p & & * p ++! = '&'); //к следующей паре id /pos
}
ответил pakled 23 Jam1000000amTue, 23 Jan 2018 02:34:57 +030018 2018, 02:34:57
1

jfpoilpret предоставил отличный ответ для синтаксического анализа последовательной команды на Arduino. Однако Attiny85 не имеет двунаправленного сериала - необходимо использовать SoftwareSerial. Так вы используете тот же самый код для Attiny85

 #include <SoftwareSerial.h>

//Вычисление на основе максимального размера ввода, ожидаемого для одной команды
#define INPUT_SIZE 30

//Инициализировать SoftwareSerial
SoftwareSerial mySerial (3, 4); //RX = PB3, TX = PB4

//Параметр для получения команды Serial (добавить 1 для окончательного 0)
char input [INPUT_SIZE + 1];

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

void loop () {
  //Нам нужен этот счетчик для имитации Serial.readBytes, которого нет в SoftwareSerial
  int key = 0;

  //Начать прием команды из Serial
  while (mySerial.available ()) {
    задержки (3); //Задержка для заполнения буфера, код становится неустойчивым на Attiny85 без этого по какой-либо причине
    //Не читайте больше символов, чем определено
    if (ключ <INPUT_SIZE & & mySerial.available ()) {
      input [key] = mySerial.read ();
      ключ + = 1;
    }
  }

  if (ключ> 0) {
    //Добавить окончательный 0 для завершения строки C
    input [key] = 0;

    //Прочитайте каждую пару команд
    char * command = strtok (input, "&");
    while (command! = 0)
    {
      //Разделить команду на два значения
      char * separator = strchr (команда, ':');
      if (разделитель! = 0)
      {
        //Собственно разделим строку на 2: заменим ':' на 0
        * separator = 0;
        int servoId = atoi (команда);
        ++ сепаратор;
        int position = atoi (разделитель);
      }
      //Найти следующую команду в строке ввода
      command = strtok (0, "&");
    }
  }
}

Схемы Attiny85 для номеров контактов введите описание изображения здесь

Эскиз компилируется в:

В Sketch используется 2244 байт (27%) пространства для хранения программ. Максимум 8192 байта.
Глобальные переменные используют 161 байт (31%) динамической памяти, оставляя 351 байт для локальных переменных. Максимум - 512 байт.

Таким образом, для остальной части кода достаточно места и памяти.

ответил goodevil 15 MaramThu, 15 Mar 2018 00:16:21 +03002018-03-15T00:16:21+03:0012 2018, 00:16:21
-1

Вот Arduino метод разделить строку как ответ на вопрос « Как разбить строку в подстроке? », объявленную как дубликат данного вопроса.

Цель решения - проанализировать ряд позиций GPS , зарегистрированных в файле SD-карты . Вместо того, чтобы иметь строку, полученную из Serial, строка читается из файла.

Функция StringSplit () анализирует строку sLine = "1.12345,4.56789, привет" до 3 строк sParams [0] = "1.12345", sParams [1] = "4.56789" & sParams [2] = "Привет".

  1. String sInput: строки ввода, подлежащие анализу,
  2. char cDelim: символ разделителя между параметрами,
  3. String sParams []: выходной массив параметров,
  4. int iMaxParams: максимальное количество параметров,
  5. Выход int: количество проанализированных параметров,

Функция основана на String :: indexOf () и String :: substring ():

int StringSplit (String sInput, char cDelim, String sParams [], int iMaxParams)
{
    int iParamCount = 0;
    int iPosDelim, iPosStart = 0;

    делать {
        //Поиск разделителя с помощью indexOf ()
        iPosDelim = sInput.indexOf (cDelim, iPosStart);
        if (iPosDelim> (iPosStart + 1)) {
            //Добавление нового параметра с помощью substring ()
            sParams [iParamCount] = sInput.substring (iPosStart, iPosDelim-1);
            iParamCount ++;
            //Проверка количества параметров
            if (iParamCount> = iMaxParams) {
                return (iParamCount);
            }
            iPosStart = iPosDelim + 1;
        }
    } while (iPosDelim> = 0);
    if (iParamCount <iMaxParams) {
        //Добавление последнего параметра в конец строки
        sParams [iParamCount] = sInput.substring (iPosStart);
        iParamCount ++;
    }

    return (iParamCount);
}

И использование очень просто:

String sParams [3];
int iCount, i;
String sLine;

//чтение строки из файла
sLine = readLine ();
//анализировать только если существует
if (sLine.length ()> 0) {
    //разбор строки
    iCount = StringSplit (sLine, ',', sParams, 3);
    //распечатать извлеченные параметры
    для (i = 0; i <iCount; i ++) {
        Serial.print (sParams [I]);
    }
    Serial.println ( "");
}
ответил J. Piquard 28 12016vEurope/Moscow11bEurope/MoscowMon, 28 Nov 2016 23:15:01 +0300 2016, 23:15:01

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132