Парсим POST запрос

KorPaEv
Offline
Зарегистрирован: 24.11.2014

пытаюсь  разобраться с веб интерфейсами и передачей данных на sd карту
нашел занятные примеры с использованием ajax но пока что не делаю, иду по упрощенному варианту...возникла следующая проблема, подскажите над каким решением подумать можно??
http://startingelect...lly-using-AJAX/
по этой ссылке читается POST запрос и сохраняется в переменную таким образом

if (client.available()) {   // client data available to read
char c = client.read(); // read 1 byte (character) from client
HTTP_req += c;  // save the HTTP request 1 char at a time

тут все понятно, я стал делать свой пример, у меня все данные передаются все хорошо и получается что я принимаю в эту переменную строку вида

r2=on&r3=on&r4=on&postTemp=123

где rN - это мои релешки которые я включаю через веб интерфейс, а postTemp - это значение температуры пороговое при котором я тыркаю какую то релешку, оно у меня устанавливается на странице текстбоксом

так вот когда я получил строку такого вида как ее распрарсить??
Причем строка может как вида полного когда все реле включены и температура не задана

r2=on&r3=on&r4=on&postTemp=

так и когда все реле выключены и ничего не задано

postTemp=

или когда задана температура но реле выключены

postTemp=123

как это лучше всего парсить?

я брал пример просто без порогового значения отсюда...там парсится чисто по символьно

http://zelectro.cc/E...et_shield_W5100

но для моей задачи теперь считаю что этот подход не верный - мне проще собрать сначала всю строку запроса а потом ее парсить и изменять массив состояния реле

com
Offline
Зарегистрирован: 06.09.2013

strtok() спасет гиганта мысли :)

1. считать всю строку зараз

2. разбить на куски с разделителем &

3. каждый кусок разбить с разделителем =

это, как говорится, "в лоб".

возможно, есть какие-нибудь готовые сишные библиотеки, но тут делов на десяток строчек

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

Точнее даже 24 пост той темы что я выше кинул, там уже кое что поправили и дефайны вынесли для определения разделителей

KorPaEv
Offline
Зарегистрирован: 24.11.2014

com пишет:

Penni пишет:

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

У меня для начала строка формируется POST методом

Читаю я ее в String

 EthernetClient client = server.available();
  if (client) {
    boolean currentLineIsBlank = true; //Признак того что текущая строка окончена
    while (client.connected()) 
    {
      while(client.available()) 
      {
        char c = client.read();
        
        if (c == '\n' && currentLineIsBlank) 
        { 
          while(client.available())
          {
            char sss = client.read();
            HTTP_req += sss;           
          } 
          Serial.println(HTTP_req); 
          HTTP_req = "";   
          client.println("HTTP/1.0 200 OK");
          client.println();
          generatePage(client);  
          client.stop();
        }
        else if (c == '\n') 
        {
          currentLineIsBlank = true;
        } 
        else if (c != '\r') 
        {
          currentLineIsBlank = false;
        }
      }
    }

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

http://forum.arduino.cc/index.php/topic,59917.html

у меня строка вида

r0=on&r1=on&r3=on&postTemp=100500

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

 

Еще отдельно актуален вопрос...

Я заношу запрос в строку типа String..не хочется пользоваться типом, много жрет места...но когда я определяю указатель на char то у меня в Serial ничего не выводит...я так понимаю потому как указатель на char висит тупо в памяти, а мне видимо надо создать еще массив указателей и его выводить или я ошибаюсь?

com
Offline
Зарегистрирован: 06.09.2013

KorPaEv пишет:

Я заношу запрос в строку типа String..не хочется пользоваться типом, много жрет места...но когда я определяю указатель на char то у меня в Serial ничего не выводит...я так понимаю потому как указатель на char висит тупо в памяти, а мне видимо надо создать еще массив указателей и его выводить или я ошибаюсь?

тупо он висит или не тупо - без кода не определить

KorPaEv
Offline
Зарегистрирован: 24.11.2014

com пишет:

KorPaEv пишет:

Я заношу запрос в строку типа String..не хочется пользоваться типом, много жрет места...но когда я определяю указатель на char то у меня в Serial ничего не выводит...я так понимаю потому как указатель на char висит тупо в памяти, а мне видимо надо создать еще массив указателей и его выводить или я ошибаюсь?

тупо он висит или не тупо - без кода не определить

вот тестовый код

char* HTTP_req = "";

while(client.available())
          {
            char sss = client.read();
            HTTP_req += sss;           
          } 
          Serial.println(HTTP_req); 
          HTTP_req = "";

Serial при выводе в консоль пустой, выходит что HTTP_req пустой.

но когда пишу

String HTTP_req = "";

while(client.available())
          {
            char sss = client.read();
            HTTP_req += sss;           
          } 
          Serial.println(HTTP_req); 
          HTTP_req = "";

все работает.

Datak
Offline
Зарегистрирован: 09.10.2014

String - это строка. Ну, то есть, настроящая текстовая строка, из разных буковок и циферок.

char* - это указатель на одну из таких буковок - отдельную, или в составе какой-то строки - это сейчас не важно. Но прибавлять буквы к указателю - бессмысленно. Прибавлять надо к тому, на что он указывает.

KorPaEv
Offline
Зарегистрирован: 24.11.2014

Datak пишет:

String - это строка. Ну, то есть, настроящая текстовая строка, из разных буковок и циферок.

char* - это указатель на одну из таких буковок - отдельную, или в составе какой-то строки - это сейчас не важно. Но прибавлять буквы к указателю - бессмысленно. Прибавлять надо к тому, на что он указывает.

так вот как разщ если я наприаер пишу такое

char* ch = "werhweifjweilf ifjsifj ofjsroifj op";

у меня фактически это как раз указатель на набор символов, так?

могу ли я к этому набору прибавить новый символ считанный...у меня же сейчас char указывает на то что в кавычках или нет?

Datak
Offline
Зарегистрирован: 09.10.2014

KorPaEv пишет:

char* ch = "werhweifjweilf ifjsifj ofjsroifj op";

у меня фактически это как раз указатель на набор символов, так?

Точно, так. Сейчас ch - это стрелочка указывающая на начало строки. А саму строку компилятор положил в память, куда-то там, куда ему захотелось.

Цитата:

могу ли я к этому набору прибавить новый символ считанный...у меня же сейчас char указывает на то что в кавычках или нет?

Да, можешь, но с поправками.

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

Во-вторых, для такого типа компилятор не знает операции "+", поэтому записывать придётся вручную, так же как в любую другую переменную.
Записывать можно двумя способами. Можно оставить указатель на месте, а нужный символ строки выбирать с помощью индекса в скобках.





























ch[0] = 'A';
ch[1] = 'B';
ch[2] = 'C';
 

А можно двигать сам указатель.





























*ch = 'A';
ch = ch + 1;
*ch = 'B';
ch = ch + 1;
*ch = 'C';

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





























*ch++ = 'A';
*ch++ = 'B';
*ch = 'C';

При такой "ручной" записи всегда надо следить, чтобы случайно не вылезти за пределы выделенной строки.

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

-------

Upd: А, ещё. Лучше выделять буфер с указанием необходимого размера.













char ch_1[20];         // неинициализированный двадцатибайтовый буфер
char ch_2[20] = "ABC"; // такой же буфер, но с записанной строкой "ABC"; хвост буфера остаётся неинициализированным

Только, при таком объявлении,  ch  это уже не указатель, а имя массива. В общем, пользоваться можно почти так же, но разница в том, что его не получится никуда сдвинуть. Это не страшно - если хочется двигать, можно создать указатель-копию.













char* p = ch;
*p++ = 'A';
.......

Во, целую лекцию написал. Надеюсь, всё понятно. Я старался. :)

KorPaEv
Offline
Зарегистрирован: 24.11.2014

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

Нужны какие то лекции с простым объяснением указателей и массивов указателей (** или *** и т.д)

KorPaEv
Offline
Зарегистрирован: 24.11.2014

Начал разбираться, почитал форум библиотеки регулярных выражений, возникли сразу вопросы, кое что не получается...
1 - у меня на входе строка типа String, начал писать функцию парсинга строки с использованием библы
2 - при использовании библиотеки на вход нужен указатель на строку символов, начал конвертировать String в char* и тут проблема я не допонимаю немного в чем именно трудность

void ParseRequestStr(String reqStr)
{
        MatchState ms; //Объявил объект библиотеки
        unsigned int index = 0;
        String rStr = reqStr; //Вот моя входная строка
        char bufferReqStr[sizeof(rStr)]; //тут судя по примеру создаем буфер нужного размера входной строки
        rStr.toCharArray(bufferReqStr, sizeof(bufferReqStr)); //Конвертирую строку в массив символов
        char * str = rStr; //ВОТ ТУТ ПОЧЕМУ Я НЕ МОГУ СОЗДАТЬ УКАЗАТЕЛЬ НА МОЙ МАССИВ СИМВОЛОВ????
        ms.Target(str); //Далее по примеру передаю указатель на строку символов
   
        while (true)
        {
          //r0=on&r1=on&postTemp=123
          char result = ms.Match ("(%a+)(%d)=(%-?%a+)", index);
          if (result == REGEXP_MATCHED)
          {
                Serial.print ("Matched on: ");
                Serial.println (ms.GetMatch(bufferReqStr));
                Serial.println ("Captures:");
                for (int j = 0; j < ms.level; j++)
                   Serial.println (ms.GetCapture(bufferReqStr, j));
                index = ms.MatchStart + ms.MatchLength;
          }
          else
                break;
        }
        Serial.println(str);
}

как раз у меня проблема в конвертации chararray в char* почему явно не могу сказать указателю, что вот моя строка символов?
и еще с самой регуляркой вопрос
моя строка вида

r0=on&r1=on&postTemp=123

регулярка ее разберет из примера с форума вот так

"(%a+)(%d)=(%-?%a+)"

тут как я понял читаем сначала произвольное кол-во символов, далее идет цифра, все как у меня, потом =, а что дальше?? у меня может быть как on значение так и типа float (температура)....следовательно вот тут %-?%a+ читается только символы (прочитается on) а у меня помимо on может быть и значение температуры...
и еще так и не понял что значит -?

 

http://forum.arduino.cc/index.php?topic=59917.15

пост номер 5

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015
MatchState ms;
unsigned int index = 0;
char bufferReqStr[reqStr.length()+1];
char bufferStr[reqStr.length()+1];
reqStr.toCharArray(bufferStr, reqStr.length()+1);
ms.Target(bufferStr);

while (true)
{
  //r0=on&r1=on&postTemp=123
  char result = ms.Match ("(%a+)(%d)=(%-?%a+)", index);
  if (result == REGEXP_MATCHED)
  {
    Serial.print ("Matched on: ");
    Serial.println (ms.GetMatch(bufferReqStr));
    Serial.println ("Captures:");
    for (int j = 0; j < ms.level; j++)
      Serial.println (ms.GetCapture(bufferReqStr, j));
    index = ms.MatchStart + ms.MatchLength;
  }
  else
    break;
}
Serial.println(bufferStr);

На выходе получите:

Matched on: r0=on
Captures:
r
0
on
Matched on: r1=on
Captures:
r
1
on
r0=on&r1=on&postTemp=12
 
Такое регулярное температуру не обработает надо вспоминать регулярки, давно с ними не работал, точнее работал но с элементарными совсем.
 
*UPD*
bufferReqStr нужен для работы регулярных выражений
bufferStr для преобразования String в chararray
KorPaEv
Offline
Зарегистрирован: 24.11.2014

Я тогда не пойму в чем моя ошибка??

почему такая конструкция не работает?

char bufferReqStr[sizeof(rStr)];
rStr.toCharArray(bufferReqStr, sizeof(bufferReqStr));
ms.Target(rStr);

UPD:

у нас же

bufferReqStr и bufferStr в вашем коде содержат одинаковый набор символов так?
Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Вы во второй строке преобразуете rStr в массив, в а 3й строке в качестве аргумента передаете опять же rStr, а надо преобразованный массив. И sizeof тут работать не будет, вы получите размер указателя а не размер строки, надо length использовать.

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

Нет, bufferReqStr используется только при обработке регулярных выражений (GetMath, GetCapture) в этот буфер складываются результаты сравнений грубо говоря. А в bufferStr лежит наша преобразованная строка (из String в chararray)

KorPaEv
Offline
Зарегистрирован: 24.11.2014

Penni пишет:

Вы во второй строке преобразуете rStr в массив, в а 3й строке в качестве аргумента передаете опять же rStr, а надо преобразованный массив. И sizeof тут работать не будет, вы получите размер указателя а не размер строки, надо length использовать.

не понимаю одну вещь все равно

я попробовал ваш вариант, все работает

MatchState ms;
    unsigned int index = 0;
    char bufferReqStr[reqStr.length() + 1];
    char bufferStr[reqStr.length()+1];
    reqStr.toCharArray(bufferStr, reqStr.length() + 1);
    ms.Target(bufferStr);

почему я не могу использовать везде содин буфер?

MatchState ms;
    unsigned int index = 0;
    char bufferReqStr[reqStr.length() + 1];
    reqStr.toCharArray(bufferReqStr, reqStr.length() + 1);
    ms.Target(bufferReqStr);

 

KorPaEv
Offline
Зарегистрирован: 24.11.2014

Penni пишет:

Нет, bufferReqStr используется только при обработке регулярных выражений (GetMath, GetCapture) в этот буфер складываются результаты сравнений грубо говоря. А в bufferStr лежит наша преобразованная строка (из String в chararray)

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

спасибо.

А вообще видимо надо почитать много чего про char, потому как до этого я думал что создавая массив

char bufferReqStr[reqStr.length() + 1]; у меня просто создастся массив размера моей строки, оказывается сама строка копируется в этот массив с учетом ее размера...до этого я считал что он содержит непонятные данные под которые выделяется память указанного размера.

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

Ну если вкратце, то они затрут друг друга. И мы не получим желаемого результата

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

Всё правильно, char bufferReqStr[reqStr.length() + 1]; не копирует строку, он просто выделяет указанное количество памяти и всё. А "копирование" происходит при вызове toCharArray

KorPaEv
Offline
Зарегистрирован: 24.11.2014

Penni пишет:

Всё правильно, char bufferReqStr[reqStr.length() + 1]; не копирует строку, он просто выделяет указанное количество памяти и всё. А "копирование" происходит при вызове toCharArray

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

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

Да, именно так.

При использовании вот такой регулярки (%a+)(%d-)=([%a%d]+) на выходе получим:
 
Matched on: r0=on
Captures:
r
0
on
Matched on: r1=on
Captures:
r
1
on
Matched on: postTemp=123
Captures:
postTemp
 
123
r0=on&r1=on&postTemp=123
 
т.е. ms.level у нас 2 (0, 1, 2) получается что 0 - имя параметра, 1 - номер параметра (для температуры пусто т.к. у нас оно без номера) и 2 - значение параметра для реле это on\off для температуры собственно температура
KorPaEv
Offline
Зарегистрирован: 24.11.2014

Благодарю! Шикарно теперь можно парсить строки сложные, вроде понял!

(%a+)(%d-)=([%a%d]+)

Только почему d-  ??

Я так понимаю a+ учитывает что может быть множество символов

d определяет число именно, но почему "-"???

И опять же d только работает я потестил с целочисленными данными

Если задать порог с плавающей точкой, то получаем округленное значение.

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

Ну - это значит 0 или более вхождений нежадное. т.е. после r есть цифра, а после postTemp нет, вот поэтому 0 или более. Да работает только пока разделитель не встретится в виде точки или запятой для дробных, поэтому надо немного переписать и добавить в квадратные скобки точку. Сейчас на работе времени нет, вечером дома гляну как там лучше переписать регулярку.

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

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

(%a+)(%d-)=([%a%d\.]+)

KorPaEv
Offline
Зарегистрирован: 24.11.2014

Penni пишет:

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

(%a+)(%d-)=([%a%d\.]+)

благодарю, попробую!

valeraba
Offline
Зарегистрирован: 08.09.2014

Не хочу сбивать с толку, но может, если нужно удалённое управление, сгодится вариант облачного сервиса?
Тогда ничего парсить не придётся, сайт сам сгенерирует исходный код программы.
Останется только вставить свой полезный код в каждый из обработчиков.
И крапеть над визуальными интерфейсами не потребуется, воспользуетесь готовыми компонентами.
Реально, всю работу можно будет сделать в течении нескольких минут.
Если заинтересуетесь, но что-то будет непонятно, то я помогу.

http://samde.ru/ru/index.html