Разбор тестовой строки
- Войдите на сайт для отправки комментариев
Чт, 04/04/2013 - 09:14
Здравствуйте, коллеги!
Использую Ethernet-shield. Пытаюсь разобрать параметр, кторый передается в URL, например так: http://192.168.3.5/?2:1,15:0,11:2 . Тут 192.168.3.5 - это адрес шилда. Сервер работает. Привожу код:
char* param;
char* buffer;
if (param = ethernet.serviceRequest())
{
ethernet.print("<H2>");
ethernet.print(param);
ethernet.print("</H2>");
buffer=strtok(param,",");
ethernet.print("<br>");
ethernet.print(buffer);
ethernet.print("</br>");
// while(buffer!=NULL);
{
buffer=strtok(NULL,",");
// num =atoi(buffer);
ethernet.print("<br>");
ethernet.print(buffer);
ethernet.print("</br>");
}
ethernet.respond();
}
ethernet.print(buffer) в строке 07 выводит ?2:1,15:0,11:2. Т.е. параметр передается. А вот парсинг дальше не работает как мне нужно. Я хочу распарсить параметры при помощи функции strtok. При первом и втором вызове она отрабатывает как положено, но вот заставить работать ее в цикле
while(buffer!=NULL);
я не могу - при отображении страницы Arduino виснет до reset'a. Подозреваю, что не работает условие buffer=NULL и она уходит в бесконечны цикл. Работать с указателями я не умею. Прошу помощи.
Вот так работает.
void loop() { unsigned long time1, time2; time1 = micros(); char* param; if (param = ethernet.serviceRequest()) { ethernet.print("<H2>"); ethernet.print(param); ethernet.print("</H2>"); ethernet.print("<br>"); ethernet.print("Output state is:"); ethernet.print(curr_output_state); ethernet.print("</br>"); param++; param=strtok(param,","); ethernet.print("<br>"); ethernet.print(param); ethernet.print("</br>"); for (int i = 0; i < 4; i = i + 1) { param=strtok(NULL,","); ethernet.print("<br>"); ethernet.print(param); ethernet.print("</br>"); } ethernet.print("<br>"); ethernet.print("Time is:"); time2 = micros(); ethernet.print(time2-time1); ethernet.print("</br>"); ethernet.respond(); }Но это не цикл while, а тупой for на фиксированное число параметров. И, естетственно, если параметров больше, чем 4, то он их проигнорирует, а если меньше - то выдает так:
2:1
15:0
11:2
а
а
Time is:4656
Как все-таки правильно использовать strtok?
Сделал так:
ethernet.print("<br>"); ethernet.print("Output state is:"); ethernet.print(curr_output_state); ethernet.print("</br>"); param++; param=strtok(param,","); ethernet.print("<br>"); ethernet.print(param); // ethernet.print("</br>"); // for (int i = 0; i < 4; i = i + 1) do { param=strtok(NULL,","); ethernet.print("<br>"); ethernet.print(param); // ethernet.print("</br>"); } while(param!=NULL); ethernet.print("<br>"); ethernet.print("Time is:"); time2 = micros(); ethernet.print(time2-time1); ethernet.print("</br>"); ethernet.respond();И все равно добавляется одна дополнительная итерация цикла (видно как 'a'):
Output state is:-32768
2:1
15:0
11:2
2:1
15:0
а
Time is:4560
Что делать? Подскажите?
>например так: http://192.168.3.5/?2:1,15:0,11:2
Это потому что вы деалете
ethernet.print(param);не проверяя, вернул вам strtok NULL или нет.
Вам нужно, либо
do { param=strtok(NULL,","); if(param!=NULL){ ethernet.print("<br>"); ethernet.print(param); } } while(param!=NULL);Но, как на меня - выглядит коряво. Лучше вернутся к вашей изначальной идее с while циклом. И поправить ее - перенести strok в конец цикла.
char* param; char* buffer; if (param = ethernet.serviceRequest()) { ethernet.print("<H2>"); ethernet.print(param); ethernet.print("</H2>"); buffer=strtok(param,","); ethernet.print("<br>"); ethernet.print(buffer); ethernet.print("</br>"); while(buffer!=NULL); { // num =atoi(buffer); ethernet.print("<br>"); ethernet.print(buffer); ethernet.print("</br>"); buffer=strtok(NULL,","); } ethernet.respond(); }Эм, второй вариант нужно еще сократить, что-бы не делать print если даже первого токена не нашло.
char* param; char* buffer; if (param = ethernet.serviceRequest()) { ethernet.print("<H2>"); ethernet.print(param); ethernet.print("</H2>"); buffer=strtok(param,","); while(buffer!=NULL); { ethernet.print("<br>"); ethernet.print(buffer); ethernet.print("</br>"); buffer=strtok(NULL,","); } ethernet.respond(); }А можно попытатся так поизголятся (но я не уверен что такой код можно назвать красивым), сделать это в виде for-а. Правда не проверял (нет щас ArduinoIDE под руками)
char* param; char* buffer; if (param = ethernet.serviceRequest()) { ethernet.print("<H2>"); ethernet.print(param); ethernet.print("</H2>"); for(buffer=strtok(param,","); buffer!=NULL; buffer=strtok(NULL,",") ) { ethernet.print("<br>"); ethernet.print(buffer); ethernet.print("</br>"); } ethernet.respond(); }Хм, я знаю, что это из за отсутствия проверки на NULL, но она у меня почему то не отрабатывала.
Ваш вариант с for(..) компилится. Завтра проверю на железке и отпишусь дополнительно.
Спасибо за помощь!
Хм, я знаю, что это из за отсутствия проверки на NULL, но она у меня почему то не отрабатывала.
А как она может отрабатывать, если она отсуствует? Если же вы говорите про свой первый вариант (который c while) - то всмотритесь в разницу: вы делали strtok в начале тела цикла, а я - в конце.
Супер!
Всё работает как нужно с циклом for.. (пост №5).
Большое спасибо, leshak!
В догонку вопрос:
В программе есть переменная, описанная следующим образом:
Так вот при попытке вывести ее значение
Я получаю числа больше 32768 как отрицательные. Хотя описана она как беззнаковое целое. Нужно получать целое.
Как быть? Смотреть внутрь ethernet.print?
В дебрях библиотеки нашел:
void ETHER_28J60::print(int number) { char tempString[9]; itoa(number, tempString, 10); print(tempString); }И все равно непонятно, где косяк - переопределения типа вроде нет.
Добавьте в библиотеку свой аналoгичный метод print, но с параметром типа uint16_t. Вероятно, нужно найти подходящую замену для функции itoa.
Спасибо!
Буду пробовать - мои познания в С пока оставляют желать лучшего.
может быть ltoa
В дебрях библиотеки нашел:
void ETHER_28J60::print(int number) { char tempString[9]; itoa(number, tempString, 10); print(tempString); }И все равно непонятно, где косяк - переопределения типа вроде нет.
Переопределение - уж простите за тавтологию - в самом определении функции. Те два байта, что вы передаете при вызове, она воспринимает как двухбайтовое целое со знаком (int). Так что либо ищите функцию print, принимающую параметр long int (ну и соответственно передавайте ей аргумент long int), либо пишите свою, принимающую параметр unsigned int.
Либо - в том случае, если в библиотеке есть перегруженная функция print(unsined int), вызывайте именно ее.
Что-то вроде
ethernet.print((unsigned int)curr_output_state);
Я бы не лез что-то менять в библиотеку без крайней нужды. Выйдет новая версия - и опять меняй, даш кому-то код - будет ругатся.
imho лучше вначале конвертировать ваш Unsigned int в строку, а уж строку - кормить библиотеке.
Я бы поптылся сделать это с помощью sprintf. Правда пару раз натыкался, что в арудино он полукоцнутый (в целях экономии), с double не работал вроде, нужно пробовать как с uint16 он справится. Если справится - то возможно даже удобней будет, обычно же не просто число, а еще и какоей-то текст выводить нужно с ним поясняющий. sprintf в этом очень удобен.
P.S. Судя по куску из либы , вы юзаете библиотеку enc28j60 имитирующую интерфейс визнетовской библиотеки. Я находил, годик назад, вот такую https://github.com/turicas/Ethernet_ENC28J60/ но была очень сырой и автор забросил. Как ваша, стабильно работает? Это "она позрослела" или у вас какая-то другая? Если другая - ссылкой не поделитесь?
Leshak, по поводу библиотеки: именно по ссылке на этом форуме по вашей рекомендации брал на gthub'e. Точный URL не помню, но в библиотеке в заголовке написано следующее:
ETHER_28J60.cpp - Ethernet library
Copyright (c) 2010 Simon Monk. All right reserved.
Работает на удивление очень хорошо и просто. И быстро. И размер скетча не сильно вырос. И это не библиотека, по приведенной выше ссылке.
Ссылкой, соответственно не поделюсь, но могу выслать архив с библиотекой целиком.
Вернемся к тексту. Вернее к разбору параметров. Напомню: параметр передается в виде http://192.168.3.5/?12:1,15:0,11:2,2:1,77:4
Т.е. пары "порт:значение" , разделенные зяпятыми. С помощью leshak http://arduino.ru/forum/programmirovanie/razbor-testovoi-stroki#comment-27718 я выделил пары. Теперь мне необходимо выделить в каждой паре "порт:значение" "порт" и "значение". И тут я попадаю в тупик. Видимо не до конца понимая разницу работы с char и char * (указатель). Вот код:
void loop() { RealTimer.run(); // Тут делать нечего - только инициируем таймер 8) if (param = ethernet.serviceRequest()) web(); // Если есть URL-запрос, то обработаем его } //END OF PROGRAM void web() { unsigned long time1, time2; time1 = micros(); int port, value; char sym; // Serial.write(param); param++; //Пропустим первый "?" в URL'е - он нам не нужен. // for(buffer=strtok(param,","); buffer!=NULL; buffer=strtok(NULL,",") ) { ethernet.print("<br>"); ethernet.print(buffer); } } param[0]=0; //Очистим после обработки глобальный буфер строки параметров ethernet.respond(); }В строке 24 ethernet.print(buffer); корректно выводятся пары "порт:значение", но если я пытаюсь по аналогии разобрать пары "порт:значение" при помощи функции strtok, то она разрушает исходную строку. Как передать строку в функцию, чтобы передавалась копия? У меня передается указатель и манипуляции со строкой разрушают ее основную копию. Как сделать это правильно?
for(buffer=strtok(param,","); buffer!=NULL; buffer=strtok(NULL,",") ) { ethernet.print("<br>"); ethernet.print(buffer); buffer3=buffer; ferr = false; buffer2=strtok(buffer,":"); // sym=strtoul(buffer,NULL,0); ethernet.print("------"); ethernet.print(buffer2); // sym=strtoul(buffer,NULL,0); buffer2=strtok(NULL,":"); ethernet.print("--:--"); ethernet.print(buffer2); buffer=buffer3; }Этот фрагмент при вызове http://192.168.3.5/?12:1,15:0,11:2,2:1,15,77:4 выдает
12:1------12--:--1
Output state is:-16642
Time is:200
Т.е. после первого вызова buffer2=strtok(buffer,":"); портит исходную строку. Ну не знаю что делать уже!
Плюнул на все функции и написал разбор сам. Правда без проверок на корректность, но размер скетча уменьшился сильно, по сравнению с применением atoul и т.п.
param++; //Пропустим первый "?" в URL'е - он нам не нужен. // for(buffer=strtok(param,","); buffer!=NULL; buffer=strtok(NULL,",") ) { ethernet.print("<br>"); ethernet.print(buffer); port = (*(buffer) - 48); if (*(buffer+1)!=':') { port = port *10 + (*(buffer+1)-48); value = (*(buffer+3)-48); } else value = (*(buffer+2)-48); ethernet.print("==>"); ethernet.print(port); ethernet.print("==>"); ethernet.print(value); }Может все-таки есть более красивое решение? Меня смущает в этой конструкции время обработки - почти 1000мкс на 7 параметров..
p.s. Таки разобрался с указателями.
Нужно смотреть описание функции strtok, на ней вложенный разбор будет сделать сложно, поскольку эта функция (strtok) работает со статическим буфером внутри себя. Вы нашли другой вариант - прекрасно!
В строке 24 ethernet.print(buffer); корректно выводятся пары "порт:значение", но если я пытаюсь по аналогии разобрать пары "порт:значение" при помощи функции strtok, то она разрушает исходную строку. Как передать строку в функцию, чтобы передавалась копия? У меня передается указатель и манипуляции со строкой разрушают ее основную копию. Как сделать это правильно?
IMHO вы путь неверный выбрали. Вам нужно не "разбил строку на части по признаку запятая", а потом в этих частях искать двоеточия. А изначально идти "ищем двоеточие"/"ищем запятую"/"ищем двоеточие"/"ищем запятую",
вообщем по очереди дергать strtok(NULL,':') и strtok(NULL,',') . То что вернулось от первого - ваш порт, то что от второго - значение. И все :) Телемаркет.
Если бы я писал, то во первых разделил бы сам парсинг и вывод того что напарсили. Мухи отдельно, котлеты отдельно. А то, потом, у вас в парнсинг и бизнес логика влезет. А потом "черт ногу сломит".
Во вторых - вынес бы парсинг в отдельную функцию. Которая ничерта не знает "откуда взялась строка" из ether-нета или от телепатов. Так и тестить и на другое железо легче перейти будет (и другим, кстати легче будет ваш скетч пускать, даже тем у кого нет вашего шилда).
Вообщем примерно так:
// структра описывающие наш параметр struct port_param_t{ int port; int value; }; #define MAX_PARAMS 20 // сколько параметров максимально мы умеем парсить port_param_t params[MAX_PARAMS]; // в этот массис будем сохранять наши парсенные параметры byte parsedParams=0; // сколько параметров нам удалось напарсить void setup(){ Serial.begin(57600); char* inputString="h ttp://192.168.3.5/?2:1,15:0,11:2"; // наши тестовые данные // выводим что собираемся парсить Serial.print("input '");Serial.print(inputString); Serial.println("'"); // парсим parseParams(inputString); // выводим что получилось printParams(); } // парсид входящую строку в массив params[] и устанавливает parsedParam в количество прочитанных элементов void parseParams(char* inputString){ parsedParams=0; // пока ничего не напарсили char* buffer=strtok(inputString,"?"); // лучше так проверять/пропускать вопросилово if(buffer!=NULL){ for(buffer=strtok(NULL,":"); buffer!=NULL; buffer=strtok(NULL,":") ) { // парсим порт params[parsedParams].port=atoi(buffer); // вам же навернео еще и сразу в int захочется конвертнуть, раз это цифры // парсим значение if( (buffer=strtok(NULL,",")) !=NULL) params[parsedParams].value=atoi(buffer); else return ; // фигня какая-то, порт есть, а значения нет, прекращаем парсинг parsedParams++; // отмечаем сколько удалось распарсить if(parsedParams>MAX_PARAMS-1)return; // больше нет места куда сохранять парсенное. } } } // Выводит в Serial массив parsedParams[] void printParams(){ for(byte i=0;i<parsedParams;i++){ // TODO: всю эту кучу принтов можно заменить одним sprintf Serial.print("Port");Serial.print(params[i].port,DEC); Serial.print("="); Serial.println(params[i].value,DEC); } } void loop(){ }Выведет:
Leshak - просто нет слов! Красиво, черт побери!
И идея красивая (про "," и ":"). И структурно (и в программе у меня) правильнее вынести парсинг и вывод в отдельные процедуры. Я вот тоже хотел разделить, но не нашел красивого метода, как хранить реультат парсинга. Оригинально - со структурой получилось.
Я вот и хотел у зубров такого вот плана совет.
Но больше всего меня поражает, что вы нашли время набрать и проверить текст программы. Еще и с комментариями! Спасибо огромное!
Я снимаю шляпу. Будете в Омске - пожалуйста откликнитесь- надеюсь передать поклон лично.
p.s. Еще про atoi - уж больно много она памяти жрет. Я наверное свой примитивный конвертор оставлю.
>но не нашел красивого метода, как хранить реультат парсинга
Ну вообще структуры - довольно мутная штука. Пару раз наступал на глюки с ними в ардуине. Но даже не зная их, можно было просто объявить два массива int ports[MAX_PARAMS] и int values[MAX_PARAMS] .
>Будете в Омске - пожалуйста откликнитесь- надеюсь передать поклон лично.
"Интернет открывает весь мир и запирает в одной комнате" :) Мне и на маршрутке проехать 5-ть остановок - уже событие жизни :)
>Но больше всего меня поражает, что вы нашли время набрать и проверить текст программы
На что только не пойдешь лишь бы свою основную работу не делать :)
>Еще про atoi - уж больно много она памяти жрет
Ну а что мешает написать свой ее вариант. Более урощенный? Тем более что принцип его построения - вы сами догадались в "своем парсере". Осталось только вынести его в отдельную функцию что-бы не дублировать код при парсинге порта и значения. Да и привязываться к "сколько цифр ожидается" - не сдорово. Лучше универсальную. Вообщем вот:
#define PARAM_DELIMETER "," // чем разделены у нас параметры #define NAME_VALUE_DELIMETER ":" // чем разделены у нас имя параметра и значение // структра описывающие наш параметр struct port_param_t{ byte port; byte value; }; #define MAX_PARAMS 20 // сколько параметров максимально мы умеем парсить port_param_t params[MAX_PARAMS]; // в этот массис будем сохранять наши парсенные параметры byte parsedParams=0; // сколько параметров нам удалось напарсить void setup(){ Serial.begin(57600); char* inputString="h ttp://192.168.3.5/?2:1,15:0,11:2"; // наши тестовые данные // выводим что собираемся парсить Serial.print("input '");Serial.print(inputString); Serial.println("'"); // парсим parseParams(inputString); // выводим что получилось printParams(); } // парсид входящую строку в массив params[] и устанавливает parsedParam в количество прочитанных элементов void parseParams(char* inputString){ parsedParams=0; // пока ничего не напарсили char* buffer=strtok(inputString,"?"); // лучше так проверять/пропускать вопросилово if(buffer!=NULL){ for(buffer=strtok(NULL, NAME_VALUE_DELIMETER); buffer!=NULL; buffer=strtok(NULL, NAME_VALUE_DELIMETER) ) { // парсим порт params[parsedParams].port=parseByte(buffer); // вам же навернео еще и сразу в int захочется конвертнуть, раз это цифры // парсим значение if( (buffer=strtok(NULL,PARAM_DELIMETER)) !=NULL) params[parsedParams].value=parseByte(buffer); else return ; // фигня какая-то, порт есть, а значения нет, прекращаем парсинг parsedParams++; // отмечаем сколько удалось распарсить if(parsedParams>MAX_PARAMS-1)return; // больше нет места куда сохранять парсенное. } } } // Выводит в Serial массив parsedParams[] void printParams(){ for(byte i=0;i<parsedParams;i++){ // TODO: всю эту кучу принтов можно заменить одним sprintf Serial.print("Port");Serial.print(params[i].port,DEC); Serial.print("="); Serial.println(params[i].value,DEC); } } byte parseByte(char* inputStr){ byte val=0; while( (*inputStr)>='0' && (*inputStr)<='9'){ // идем по строке пока у нас цифры попадаются val=val*10+((*inputStr++)-'0'); // дописываем ноль к текущем val (десятичный сдвиг влево) и прибавляем новую цифру, и сдвигаемся к следующему символу } return val; } void loop(){ }Разница с прошлым скетчем:
1. atoi заменен на parseByte()
2. В структуре int-ты заменил на byte (я так понял больших чисел у нас нет), а память экономить вы хотите :)
3. Вынес чем разделяются параметры и значение в дефайны (первые две строки). Что-бы потом легче было применить для парсинга нормальных урлов вида htt p://domain.com/?param1=value1¶m2=value¶m3=value . Заменил в дефане запяты и двоеточия на равно и амперсанд - и не нужно по коду лазить.
Найденные мной ошибки:
Cтрокa 39 должна выглядеть так:
for(buffer=strtok(buffer, NAME_VALUE_DELIMETER); buffer!=NULL; buffer=strtok(NULL, NAME_VALUE_DELIMETER) ) {Первый раз в skrok нужно указать строку. Без этого не работало.
И есть еще логическая ошибка. Сразу заподозрил, и она проявилась: при разборе строки "http://192.168.3.5/?1:12,3,6:2". Ошибки не выдает. А вывод такой:
Port1=12
Port3=2
AllOK! Out state:0
Т.е. пропускает просто порт до следующего параметра и все! Видимо все-таки нужно сначала парсить на пары port:value, а потом парсить отдельно каждую пару.
>Первый раз в skrok нужно указать строку.
Не нужно. Первый раз мы строку уже указали в строке 36
>Без этого не работало.
А вы не руками скетч перенабирали? Сейчас взял скетч из #24. Залил. Вывод полностью идентичен тому что было до поправок. То есть логика по сравнению с #22 - не изменилась.
>И есть еще логическая ошибка
Ну это ошибкой с натяжкой можно назвать. Просто у вас получилась пара порт:значение "3,6:2" . То есть port с номером 3,6. parseByte попытался это "3,6" распарсить. И распарсил "как смог" до первого не цифрового символа. Вышло 3.
Так что это скорее не ошибка, а "можно еще доп.проверки на неправильные входные данные добавить". Но... если довить проверку всех возможных несуразностей, но... размер? Мы отказывлись от atoi для уменьшения размера. А для этого делаются какие-то предположения-упрощения-ожидания.
Можно вообще, перед тем как вообще парсить проверять сколько у нас двоеточий, сколько равенств. Вообщем тут уже от ситуации зависит насколько это все нужно.
>Cтрокa 39 должна выглядеть так:
Ну, как я говорил выше - не обязательно :) Так как она есть - тоже работает. Если уже пытатся ее упрощать - то отказом от for и переход на while. когда for у нас появился мы еще не делали проверку на "?". И первый и третий параметра for- отличались. Так как они перестали отличатся - for - уже избыточен. И то же самое можно сделать так:
while( (buffer=strtok(NULL, NAME_VALUE_DELIMETER))!=NULL ) {Сделал, как писал в пердыдущем посте: выделил разбор на пары "Порт:значение" и разбор внутри пары. Наступил на грабли, как и в прошлый раз: разрушается строка в подпрограмме parsePortValue.
//Процедура обработки http-запроса void web() { unsigned long time1, time2; time1 = micros(); ferr = false; parseParams(param); // Парсим полученный URL printParams(); // выводим что получилось /* for(buffer=strtok(param,","); buffer!=NULL; buffer=strtok(NULL,",") ) { // ethernet.print("<br>"); // ethernet.print(buffer); //В buffer находится строка "порт:значение". Заносим из нее значения в переменные port и value //ПРОВЕРКИ НА КОРРЕКТНОСТЬ НЕТ!!! Необходимо проверять корректность при заполнении строки! //Предполагаю, что порт может быть двух- или однозначным, значение - однозначным. port = (*(buffer) - 48); //Первая цифра - номер порта if (*(buffer+1)!=':') { //Если второй символ так же цифра, то port = port *10 + (*(buffer+1)-48); //первый был - десятки, добавим единицы value = (*(buffer+3)-48); //Четвертый, в таком случае - состояние порта } else value = (*(buffer+2)-48); //Если второй символ - разделитель ':', тогда третий - состояние порта if (port > 64)ferr = true; //Проверка на корректность введенных значений номера порта if (value > 2)ferr = true; //Проверка на корректность введенных значений параметра порта //Управление нагрузкой if (ferr==false){ //Если прочитанне значения корректны, то применим их switch (value) { case 0: DisableOutputState(port); break; case 1: EnableOutputState(port); break; case 2: ToggleOutputState(port); break; default: break; } } //Вывод отладочной информации // ethernet.print("<BR>"); ethernet.print(port); ethernet.print("==>"); ethernet.print(value); if (ferr) ethernet.print("ERROR!"); } ethernet.print("<br>"); */ if (ferr) ethernet.print("ERROR!"); else ethernet.print("AllOK!"); ethernet.print(" Out state:"); ethernet.print(curr_output_state); ethernet.print("</br>"); ethernet.print("<br>"); ethernet.print("Time is:"); time2 = micros(); ethernet.print(time2-time1); param[0]=0; //Очистим после обработки глобальный буфер строки параметров ethernet.respond(); } // парсит входящую строку в массив params[] и устанавливает parsedParam в количество прочитанных элементов void parseParams(char* inputString){ parsedParams=0; // пока ничего не напарсили, число распознанных переменных = 0 char* buffer=strtok(inputString,"?"); //Параметры для разбора команды в URL'e. Пропустим "?" в URL'е - он нам не нужен. if(buffer!=NULL){ //Бъем на пары "Порт:значение", разделенные PARAM_DELIMETER for(buffer=strtok(buffer, PARAM_DELIMETER); buffer!=NULL; buffer=strtok(NULL, PARAM_DELIMETER) ) { // парсим значения ethernet.print(buffer); ethernet.print("<br>"); parsePortValue(buffer); ethernet.print(buffer); ethernet.print("<br>"); // // ethernet.print(params[parsedParams].port); // // парсим значение } parsedParams++; // отмечаем сколько удалось распарсить if(parsedParams>MAX_PARAMS-1){ // больше нет места куда сохранять парсенное. ferr = true; return; } } } // Выводит в Serial массив parsedParams[] void printParams(){ for(byte i=0;i<parsedParams;i++){ // TODO: всю эту кучу принтов можно заменить одним sprintf ethernet.print("Port"); ethernet.print(params[i].port); ethernet.print("="); ethernet.print(params[i].value); } } byte parseByte(char* inputStr){ byte val=0; while( (*inputStr)>='0' && (*inputStr)<='9'){ // идем по строке пока у нас цифры попадаются val=val*10+((*inputStr++)-'0'); // дописываем ноль к текущем val (десятичный сдвиг влево) и прибавляем новую цифру, и сдвигаемся к следующему символу } return val; } // парсит распознанную пару "port:value" в массив params[] и устанавливает parsedParam в количество прочитанных элементов void parsePortValue(char* inputString){ char* buffer=strtok(inputString,":"); //Достанем значение перед ":". Это port if(buffer!=NULL){ //Получилось? // парсим порт params[parsedParams].port=parseByte(buffer); //Тогда конвертим в int // парсим значение if( (buffer=strtok(NULL,PARAM_DELIMETER)) !=NULL) //Идем дальше. Получилось? params[parsedParams].value=parseByte(buffer); //Тогда это значение else { // фигня какая-то, порт есть, а значения нет, прекращаем парсинг ferr = true; return ; } } else ferr = true; }На выводе при строке "http://192.168.3.5/?51:12,3:2" имеем:
51:12
51
Port51=12AllOK! Out state:0
Time is:236
Т.е. строка разрушилась после вызова parsePortValeu. Что-то я никак не догоню, как работает strtok.
>Cтрокa 39 должна выглядеть так:
Ну, как я говорил выше - не обязательно :) Так как она есть - тоже работает. Если уже пытатся ее упрощать - то отказом от for и переход на while. когда for у нас появился мы еще не делали проверку на "?". И первый и третий параметра for- отличались. Так как они перестали отличатся - for - уже избыточен. И то же самое можно сделать так:
while( (buffer=strtok(NULL, NAME_VALUE_DELIMETER))!=NULL ) {Leshak, у меня не работало в исходном варианте - выдавало сразу 0 и не разбирало строку. После сделанной мной замены программа заработала как нужно.
Про ошибку логическую - понял. В принципе согласен. Но ловить то ошибку нужно? А то можно вместо света в ванной комнате отопление выключить 8). И узнать об этом только завтра.
Я попробую сам, ладно? А потом критикнете мои потуги..
Таки разрушает, негодяй. Заплевывает ее нулевыми символами.
Вот, возможно вот это поможет втыкнуть "что он творит".
Функция dumpString - выводит строку, нулевой символ показывается звездочкой, и в следующей строке рисует где у нас сейчас указатель который вернул strkok
#define PARAM_DELIMETER "," // чем разделены у нас параметры #define NAME_VALUE_DELIMETER ":" // чем разделены у нас имя параметра и значение byte len; void setup(){ Serial.begin(57600); char* inputString="1234567890abc"; char* pointer=inputString; len=strlen(inputString); dumpString("[init]",inputString,pointer); // сдвигаем поинтер на три символа что-бы увидить как работает наш dump pointer+=3; dumpString("[+3]",inputString,pointer); // теперь пробуем поигратся с stkok pointer=strtok(inputString,"2"); dumpString("[inputString,2]",inputString,pointer); pointer=strtok(NULL,"4"); dumpString("[NULL,4]",inputString,pointer); pointer=strtok(NULL,"6"); dumpString("[NULL,6]",inputString,pointer); } void loop(){ } // выводим строку в кодах, не обращая внимание на нулевые символы void dumpString(char* comment, char* input,char* p){ // выводим коментарий что это за строка Serial.println(); Serial.println(comment); for(byte i=0;i<=len;i++){ char ch=*(input+i); // текущий символ if(ch)Serial.write(ch); else Serial.write('*'); // вместу нулевого - выводим * что-бы видеть его } Serial.println(); // На второй стороке символом | покажем текущую позицию указателя p for(byte i=0;i<=len;i++){ char* pos=input+i; if(pos==p)Serial.write("|"); else Serial.write(' '); } Serial.println(); }Выводит
[init] 1234567890abc* | [+3] 1234567890abc* | [inputString,2] 1*34567890abc* | [NULL,4] 1*3*567890abc* | [NULL,6] 1*3*5*7890abc* |Можете попробовать поэксперементировать с разными строками и вызовами strok и смотреть "что сейчас у нас в строке" и куда счас указатель указывает :) (соответсвенно дальнейший парсинг - будет идти правее него).
Leshak, у меня не работало в исходном варианте - выдавало сразу 0 и не разбирало строку. После сделанной мной замены программа заработала как нужно.
не верю. чудес не бывает. значит вы внесли еще какие-то изменения в код из #24. Я вот взял его, из форума, не откуда-то из своего файла. Скопировал и запустил, не меняя ни одной буквы - работает.
> не работало в исходном варианте
Вы так и не ответили. Насколько этот вариант "исходный"? Вы руками его набирали или копировали через кнопку в верхнем правом углу кода?
>не работало в исходном варианте
Ну вот смотрите, я взял код из #24 и выкинул структуры, массивы, парсинг чисел т.п. просто разбиваю на токены и сразу вывожу обыкновенным Serial.print, что-бы видеть "как строку разбило".
#define PARAM_DELIMETER "," // чем разделены у нас параметры #define NAME_VALUE_DELIMETER ":" // чем разделены у нас имя параметра и значение void setup(){ Serial.begin(57600); char* inputString="h ttp://192.168.3.5/?2:1,15:0,11:2"; // наши тестовые данные // выводим что собираемся парсить Serial.print("input '");Serial.print(inputString); Serial.println("'"); // парсим parseParams(inputString); } // парсид входящую строку в массив params[] и устанавливает parsedParam в количество прочитанных элементов void parseParams(char* inputString){ char* buffer=strtok(inputString,"?"); // лучше так проверять/пропускать вопросилово if(buffer!=NULL){ for(buffer=strtok(NULL, NAME_VALUE_DELIMETER); buffer!=NULL; buffer=strtok(NULL, NAME_VALUE_DELIMETER) ) { // парсим порт Serial.print("port=");Serial.println(buffer); // парсим значение if( (buffer=strtok(NULL,PARAM_DELIMETER)) !=NULL) { Serial.print("value=");Serial.println(buffer); } else return ; // фигня какая-то, порт есть, а значения нет, прекращаем парсинг } } } void loop(){}Выводит оно такое:
Теперь вносим ваше "исправление"
#define PARAM_DELIMETER "," // чем разделены у нас параметры #define NAME_VALUE_DELIMETER ":" // чем разделены у нас имя параметра и значение void setup(){ Serial.begin(57600); char* inputString="h ttp://192.168.3.5/?2:1,15:0,11:2"; // наши тестовые данные // выводим что собираемся парсить Serial.print("input2 '");Serial.print(inputString); Serial.println("'"); // парсим parseParams(inputString); } // парсид входящую строку в массив params[] и устанавливает parsedParam в количество прочитанных элементов void parseParams(char* inputString){ char* buffer=strtok(inputString,"?"); // лучше так проверять/пропускать вопросилово if(buffer!=NULL){ for(buffer=strtok(buffer, NAME_VALUE_DELIMETER); buffer!=NULL; buffer=strtok(NULL, NAME_VALUE_DELIMETER) ) { // парсим порт Serial.print("port=");Serial.println(buffer); // парсим значение if( (buffer=strtok(NULL,PARAM_DELIMETER)) !=NULL) { Serial.print("value=");Serial.println(buffer); } else return ; // фигня какая-то, порт есть, а значения нет, прекращаем парсинг } } } void loop(){}Выводит:
Вотзьмите эти два скетча. Пустите у себя и запостите тут что выводит первый и что выводит второй
А подскажите пожалуйста. Я работаю с JSM и принимаю строку
Мне из этой строки нужно изьять номер телефона.
Вот как я пробую делать
#include <SoftwareSerial.h> SoftwareSerial gprsSerial(10, 11); char currSymb; byte buff[255]; byte b; String num1 = ""; String num = ""; String inString; void setup() { Serial.begin(9600); gprsSerial.begin(9600); // Настраиваем приём сообщений с других устройств // Между командами даём время на их обработку gprsSerial.print("AT+CMGF=1\r"); delay(300); gprsSerial.print("AT+IFC=1, 1\r"); delay(300); gprsSerial.print("AT+CPBS=\"SM\"\r"); delay(300); gprsSerial.print("AT+CNMI=1,2,2,1,0\r"); delay(500); gprsSerial.print("AT+CMGDA=«DEL ALL»"); } String currStr = ""; // Переменная принимает значение True, если текущая строка является сообщением boolean isStringMessage = false; void loop() { rec(); tabl(); /* if (currStr.startsWith("+CMT")) { if (currStr == ""){ //!!!!!!!!!!!!!!! Serial.print("ok1"); Serial.print(" "); } if (!currStr.compareTo(num1)){ //!!!!!!!!!!!!!!! Serial.print("ok2"); Serial.print(" "); } } */ if ('\r' == currSymb) { if (isStringMessage) { //Serial.println(""); //Serial.println("---"); //если текущая строка - SMS-сообщение, //отреагируем на него соответствующим образом if (!currStr.compareTo("123")) { Serial.print("321"); } else if (!currStr.compareTo("Start")) { Serial.print("st"); } else if (!currStr.compareTo("Buff")) { Serial.print(""); for (byte i = 0; i < 255; i ++){ Serial.print(buff[i]); Serial.print(" "); } Serial.println(""); for (byte i = 0; i < 255; i ++){ Serial.print(buff[i], HEX); Serial.print(" "); } } else if (!currStr.compareTo("Num")) { Serial.println("num1"); } isStringMessage = false; } else { if (currStr.startsWith("+CMT")) { //если текущая строка начинается с "+CMT", //то следующая строка является сообщением isStringMessage = true; } } currStr = ""; /*gprsSerial.print("AT+CMGR=1,0"); for (byte i = 0; i < 5; i++) { char info = gprsSerial.read(); Serial.print(info); Serial.print(" "); } Serial.println();*/ //Serial.println("-/-"); } else if ('\n' != currSymb) { currStr += String(currSymb); } } void rec() { if (gprsSerial.available() > 0) { b++; currSymb = gprsSerial.read(); /* Serial.print(b); Serial.print(" "); Serial.print(currSymb); Serial.print(" "); Serial.print(currSymb, DEC); Serial.print(" "); Serial.print(currSymb, HEX); Serial.println("");*/ buff[b] = currSymb; int inChar = buff[b]; inString += (char)inChar; if(buff[b] == 0xA && buff[b-1] == 0xD) { b=0; // num = buff[10]<<4+buff[11]<<4+buff[12]<<4+buff[13];//<<4+buff[14]<<4+buff[15]<<4+buff[16]<<4+buff[17]<<4+buff[18]<<4+buff[19]<<4+buff[20]; Serial.print("String: "); Serial.println(inString); unsigned long s1, s2, s3, s4; char buffer[50]; inString.toCharArray(buffer,50); s1=atoi(strtok(buffer, " ")); s2=atoi(strtok(NULL, ",")); s3=atoi(strtok(NULL, ",")); s4=atoi(strtok(NULL, ",")); Serial.print("start: "); Serial.println(s1); Serial.print("num: "); Serial.println(s2); Serial.print("null: "); Serial.println(s3); Serial.print("date: "); Serial.println(s4); inString = ""; } } }В самом конце строчки, которыми я пробую разбить строку.
Чего уже только не пробовал делать, а хочеться чтобы было красиво и этот номер можно было сравнить с заведомо забитым в ардуину номером.
подыму тему
написал функцию парсера, работает хорошо хотя и написано не по феншую...
String str_serial = "action=1&user_id=100&act=55&id=0.";
char _rele1[] = "action"; // название
int parseGetRequest(String & str_inn, char* namePin)// примим входящую строку (String) и параметр (char), { //вернем целое положительное число (int) после = если такой параметр есть, если нет то вернет -1 int bedIndex = str_inn.indexOf(namePin); if (bedIndex > -1) { String bedString = str_inn.substring(bedIndex + strlen(namePin) + 1); int bedRangeValue = bedString.toInt(); return bedRangeValue; } else { return -1; } }может каму пригодится,
закидываем строку с параметрами и значениями, и один параметр после которого значение надо считать, получаем то что идетпосле параметра в виде int или -1 если нет такого параметра
Я что то в исходной строке не вижу rele1 или я что то не понял?
Наверное строка 3 должна выглядеть как-то так:
Или что к чему?
Я что то в исходной строке не вижу rele1 или я что то не понял?
Наверное строка 3 должна выглядеть как-то так:
Или что к чему?
Но работает же ))
спасибо, Вы правы, я все поправил.