парсинг строки

Тема в разделе "Arduino & Shields", создана пользователем zsm, 8 янв 2014.

  1. zsm

    zsm Гик

    Друзья, подскажите пожалуйста как правильно и с наименьшими затратами перевести строки в переменные? К примеру приходит в серийный порт такая стока L255R140\r как присвоить присвоить переменной L значение 255 , а переменной R значение 140?

    Это основы основ, но все примеры что встречал вообще не понимаю.
    Вот здесь все разбирается с коментами , но пример слишком сложный.
    Там создаётся куча каких то переменных и массивов вообще не понятно зачем...:)
    Посоветуйте пожалуйста что почитать на этот счёт с простыми примерами.
     
    Последнее редактирование: 8 янв 2014
  2. NR55RU

    NR55RU Гик

    Давайте я вам приведу такой максимально упрощенный пример одного из возможных вариантов.
    Вообще общая идея такого подхода была мною почерпнута из книги по разработке компиляторов как один их самых простых вариантов парсинга.
    Приведу пример кода на Си, так как чтобы не искать дуню и не делать скетч я просто написал и протестировал его на ПК.
    Сразу скажу, я максимально просто попытался сделать для лучшего понимания и объяснения идеи.

    Код (Text):

    #include <stdio.h>

    void getNumbers(char *str, char *value);

    void main()
    {
       char inputString[] = "L255R190\r";
       char *str = inputString;

       char firstCmd[4], secondCmd[4];

       while(*str != '\r' && *str != '\0')
       {
         switch(*str)
         {
           case 'L':
             getNumbers(str, firstCmd);
             printf("%s\n", firstCmd);
             break;
         
           case 'R':
             getNumbers(str, secondCmd);
             printf("%s\n", secondCmd);
             break;
         }
         str++;
       }
    }

    void getNumbers(char *str, char *value)
    {
       int i = 0;
       str++;
       while('0' <= *str && *str <= '9')
       {
         value[i] = *str;
         i++;
         str++;
       }
       value[i] = '\0';
    }
     
    И так в чем собственно суть.
    Допустим мы получили строку которую желаем распарсить (inputString), создадим указатель на первый символ строки (str), в переменные firstCmd и secondCmd мы поместим две команды.
    Если я верно понял то вас интересуют те самые числовые значения.
    НО внимание, в данных переменных будут НЕ целочисленное значение а лишь набор символов, которые потом еще требуется распарсить в целочисленное значение, благо для этого полно всяких функций и даже на ардуине в стандартной библиотеке вроде как есть что-то подобное.
    Таким образом у вас будет не число 255 а массив символов {'2','5','5'}

    И так, суть программы настолько проста что думаю проблем с ее понимания у вас не возникнет.
    Мы просто ищем ключевые символы в строке, как только указатель указывает на некоторый ключевой символ в строке то выполняется вытаскивание числовых символов функцией getNumbers() следующих за этим символом и складывание их в переменную (массив числовых символов).
    После чего двигаем указатель на следующий символ и по новой до тех пор пока не встретим '\r' или '\0'

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

    P.s. Хотя для таких вещей я всегда предпочитал регулярные выражения, но впихивать библиотеку регулярных выражений что бы распарсить простую строку на микроконтроллере это пипец какой хардкор и не факт что памяти хватит :)
     
    zsm и Unixon нравится это.
  3. zsm

    zsm Гик

    Большое спасибо за развернутый ответ с примером.Буду врубаться) Можно подобное объяснение в вики добавить , т.к. многие на этом зубы ломают наверно )
     
  4. zsm

    zsm Гик

    Перевел на понятный дуне язык вроде работает, спасибо большое)
    не совсем понял что такое "%s\n" компилятор на это ругался и пришлось убрать:)
    вообще, несмотря на отличный пример тяжело мне это всё дается...
    читаю Лебедева М.Б. CodeVisionAVR. Пособие для начинающих. Приходится каждую страницу перечитывать много раз чтобы хоть что то понять)
    Посоветуйте пожалуйста что почитать можно для старта по C++

    Код (Text):
    int L=0;
    int R=0;

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

    void loop()
    {
    char inputString[] = "L255R190\r";
    char *str = inputString;
    char firstCmd[4], secondCmd[4];

    while(*str != '\r' && *str != '\0')
      {
        switch(*str)
        {
          case 'L':
            getNumbers(str, firstCmd);
            break;
         
          case 'R':
            getNumbers(str, secondCmd);
            break;
      }
        str++;
      }
     
     
    L=atoi(firstCmd);
    R=atoi(secondCmd);
    Serial.print("L=");
    Serial.println(L);
    Serial.print("R=");
    Serial.println(R);
    delay (1000);
    }
    void getNumbers(char *str, char *value)
    {
      int i = 0;
      str++;
      while('0' <= *str && *str <= '9')
      {
        value[i] = *str;
        i++;
        str++;
      }
      value[i] = '\0';
    }
     
  5. geher

    geher Гуру

    Форматная строка функции printf для форматированного вывода на стандартную консоль (в данном случае предполагается вывод строки - %s и перевод строки сразу после). В классе Serial вроде ничего похожего (форматированного вывода) не наблюдается.

    Я изучал по простому справочнику и примерам исходных текстов.
    Народ вроде рекомендует:
    Бьярне Страуструп, "Язык программирования С++"
    Стивен Прата, "Язык программирования С++"
    В любом случае придется учитывать существенные отличия в библиотеках функций и классов, в том числе стандартных. В доступном на ардуине много чего не будет, считающегося стандартным. А с другой стороны, будет много специфического.
     
    zsm нравится это.
  6. NR55RU

    NR55RU Гик

    printf() это вам в ардуине не нужно это функция форматированного вывода из стандартной библиотеки ввода вывода Си для ПК.
    Если вы совсем новичок то рекомендую данную книгу, я ее не однократно советовал и сам Си начинал осваивать с нее, очень подробно все расписано и понятно, затронуто все по чуть-чуть в базе, что дает хорошую основу для дальнейшего изучения.
    Для начинающего лучшей книги по Си в руках не держал. Ибо ее писали именно для старт-апа и разжуют все как доктор прописал, O'Relly вообще хорошие книжечки издает.
    И я рекомендую начинать именно с Си а не с С++, дело в том что С++ это своего рода эволюция Си, в нем есть все что есть в Си но еще добавлена ООП составляющая, в итоге изучая С++ новичку сложнее ибо ему в пору бы разобраться с самыми азами и принципами а там начинают впаривать парадигмы ООП и голова идет кругом.
    Получив хорошую базу в Си вы легко освоите потом и С++ и более того микроконтролеры чаще как я успел заметить программируют на чистом Си нежели на С++ :)
    Но решать вам, это лишь мое мнение и не более :)
     
    zsm нравится это.
  7. zsm

    zsm Гик

    Спасибо понял.
     
  8. zsm

    zsm Гик

    Спасибо, книгу качаю.Буду мат часть учить потихоньку) без этого ни куда:).
    За пример еще раз спасибо, теперь смогу делать недоступные мне ранее вещи)
     
  9. NR55RU

    NR55RU Гик

    А если желаете получить действительно достойнейшую базу для любых движений в области программирования.
    То советую вам этот поистине шедевр : Код. Тайный язык информатики
    Книгу купить нельзя, искал не нашел, только качать в электронке.
    Вас там не научат программировать но то что описано там и то КАК автор это сделал, поистине бесценно и помогает в 10 раз лучше понять в последствии любой язык программирования и то как работают ПК и те же микроконтроллеры.
    Я не однократно на форуме писал что эта книга поистине ШЕДЕВР и единственная в своем роде.
    Можете читать ее в параллель с изучением Си, она вам поможет и поверьте очень сильно.
    Тому кто мне ее посоветовал я раз 10 на этом форуме высказал свою благодарность.
    Я считаю ее настолько фундаментальной и настолько базовой что я даже связывался с редакцией в надежде что у них она где нибудь завалялась чтобы купить в бумажном виде ибо она бы заняла одно из самых почетных мест на моей полке :) Но... увы .... только в электронном виде и занимает :)
     
    acos нравится это.
  10. zsm

    zsm Гик

    Уже читаю) возможно как раз от вас услышал.Книга действительно стоящая, если можно так выразиться это азбука программиста:). Многого не знал пока не открыл её, читаю не спешно, пытаюсь понять примеры , думаю не раз к ней буду возвращаться.
     
  11. NR55RU

    NR55RU Гик

    А когда доберетесь до С++, могу посоветовать данный самоучитель.
    Весьма не плох, понятен, мелкие легкие понятные примеры без переборов. Легко и приятно читается.
    Недавно его закончил читать, оставил приятные впечатления.
     
    zsm нравится это.
  12. zsm

    zsm Гик

    подскажите пожалуйста если не затруднит , как быть если строка состоит только из чисел разделённых запятыми такого вида 42,50,0
     
  13. Megakoteyka

    Megakoteyka Оракул Модератор

    Числа предполагаются целые или вещественные? Количество цифр в каждом числе одинаково или произвольно?
     
  14. zsm

    zsm Гик

    в первых двух числах передаются координаты из такой программы от -100 до 100 , 0 это конец строки по видимому.
     
  15. rav_75

    rav_75 Гик

  16. Megakoteyka

    Megakoteyka Оракул Модератор

    Конец строки - символ '\n'.

    Как я понял, поток должен выглядеть примерно так: '4' '2' ',' '5' '0' ',' '0' '\n' '4' '2' ',' '5' '0' ',' '0' '\n' ...
    Разбирать такую строку просто:
    1. нарезаем входящий поток по символу '\n'.
    2. полученные строки нарезаем по символу ',' и получаем каждый раз 3 строки с искомыми значениями.

    Что не получается сделать?
     
    zsm нравится это.
  17. zsm

    zsm Гик

    вы совершенно правы в порт приходит '4' '2' ',' '5' '0' , '0' но в конце строки не понятно что т.к. монитор порта не отображает это.. если байты смотреть то в конце строки приходит 10...
    Как раз с нарезать проблема :) , NR55RU показал как разбирать строку типа L255R190\r теперь
    успешно использую его прием.. но сам пока не могу новый тип строки осилить. Было бы здорово в ВИКИ подобную тему разобрать в разных вариациях , лично мне это сложней всего даётся..
     
  18. Megakoteyka

    Megakoteyka Оракул Модератор

    Загляните в ASCII-таблицу, код 10 (0xA) - это и есть символ перевода строки '\n'.

    Так ведь строка точно такая же. Просто вместо разделителей чисел используется запятая вместо L и R, а вместо разделителей разных строк используется '\n' вместо '\r'.
     
    zsm нравится это.
  19. zsm

    zsm Гик

    действительно :) , сейчас попробую.
     
  20. zsm

    zsm Гик


    попробовал код переделать, не понимаю что писать вместо L в
    Код (Text):
     case 'L':
            getNumbers(str, firstCmd);
    чтобы первую команду захватить ...


    Код (Text):

    int x=0;
    int y=0;
    int z=0;

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

    void loop()
    {
    char inputString[] = "42,50,0\n";
    char *str = inputString;
    char firstCmd[4], secondCmd[4], thirdCmd [4] ;

    while(*str != '\n' && *str != '\0')
      {
     
     
        switch(*str)
        {
       
          case ' ':
            getNumbers(str, firstCmd);
          break;
       
          case ',':
            getNumbers(str, secondCmd);
          break;
         
          case ',':
            getNumbers(str, thirdCmd );
          break;
      }
        str++;
      }


    x=atoi(firstCmd);
    y=atoi(secondCmd);
    z=atoi(thirdCmd);
    Serial.print("x=");
    Serial.println(x);
    Serial.print("y=");
    Serial.println(y);
    Serial.print("z=");
    Serial.println(z);
    delay (1000);
    }
    void getNumbers(char *str, char *value)
    {
      int i = 0;
      str++;
      while('0' <= *str && *str <= '9')
      {
        value[i] = *str;
        i++;
        str++;
      }
      value[i] = '\0';
    }
     
    Последнее редактирование: 14 фев 2014