Разбор тестовой строки
- Войдите на сайт для отправки комментариев
Чт, 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 и она уходит в бесконечны цикл. Работать с указателями я не умею. Прошу помощи.
Вот так работает.
Но это не цикл while, а тупой for на фиксированное число параметров. И, естетственно, если параметров больше, чем 4, то он их проигнорирует, а если меньше - то выдает так:
2:1
15:0
11:2
а
а
Time is:4656
Как все-таки правильно использовать strtok?
Сделал так:
И все равно добавляется одна дополнительная итерация цикла (видно как '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 или нет.
Вам нужно, либо
Но, как на меня - выглядит коряво. Лучше вернутся к вашей изначальной идее с while циклом. И поправить ее - перенести strok в конец цикла.
Эм, второй вариант нужно еще сократить, что-бы не делать print если даже первого токена не нашло.
А можно попытатся так поизголятся (но я не уверен что такой код можно назвать красивым), сделать это в виде for-а. Правда не проверял (нет щас ArduinoIDE под руками)
Хм, я знаю, что это из за отсутствия проверки на NULL, но она у меня почему то не отрабатывала.
Ваш вариант с for(..) компилится. Завтра проверю на железке и отпишусь дополнительно.
Спасибо за помощь!
Хм, я знаю, что это из за отсутствия проверки на NULL, но она у меня почему то не отрабатывала.
А как она может отрабатывать, если она отсуствует? Если же вы говорите про свой первый вариант (который c while) - то всмотритесь в разницу: вы делали strtok в начале тела цикла, а я - в конце.
Супер!
Всё работает как нужно с циклом for.. (пост №5).
Большое спасибо, leshak!
В догонку вопрос:
В программе есть переменная, описанная следующим образом:
Так вот при попытке вывести ее значение
Я получаю числа больше 32768 как отрицательные. Хотя описана она как беззнаковое целое. Нужно получать целое.
Как быть? Смотреть внутрь ethernet.print?
В дебрях библиотеки нашел:
И все равно непонятно, где косяк - переопределения типа вроде нет.
Добавьте в библиотеку свой аналoгичный метод print, но с параметром типа uint16_t. Вероятно, нужно найти подходящую замену для функции itoa.
Спасибо!
Буду пробовать - мои познания в С пока оставляют желать лучшего.
может быть ltoa
В дебрях библиотеки нашел:
И все равно непонятно, где косяк - переопределения типа вроде нет.
Переопределение - уж простите за тавтологию - в самом определении функции. Те два байта, что вы передаете при вызове, она воспринимает как двухбайтовое целое со знаком (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 * (указатель). Вот код:
В строке 24 ethernet.print(buffer); корректно выводятся пары "порт:значение", но если я пытаюсь по аналогии разобрать пары "порт:значение" при помощи функции strtok, то она разрушает исходную строку. Как передать строку в функцию, чтобы передавалась копия? У меня передается указатель и манипуляции со строкой разрушают ее основную копию. Как сделать это правильно?
Этот фрагмент при вызове 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 и т.п.
Может все-таки есть более красивое решение? Меня смущает в этой конструкции время обработки - почти 1000мкс на 7 параметров..
p.s. Таки разобрался с указателями.
Нужно смотреть описание функции strtok, на ней вложенный разбор будет сделать сложно, поскольку эта функция (strtok) работает со статическим буфером внутри себя. Вы нашли другой вариант - прекрасно!
В строке 24 ethernet.print(buffer); корректно выводятся пары "порт:значение", но если я пытаюсь по аналогии разобрать пары "порт:значение" при помощи функции strtok, то она разрушает исходную строку. Как передать строку в функцию, чтобы передавалась копия? У меня передается указатель и манипуляции со строкой разрушают ее основную копию. Как сделать это правильно?
IMHO вы путь неверный выбрали. Вам нужно не "разбил строку на части по признаку запятая", а потом в этих частях искать двоеточия. А изначально идти "ищем двоеточие"/"ищем запятую"/"ищем двоеточие"/"ищем запятую",
вообщем по очереди дергать strtok(NULL,':') и strtok(NULL,',') . То что вернулось от первого - ваш порт, то что от второго - значение. И все :) Телемаркет.
Если бы я писал, то во первых разделил бы сам парсинг и вывод того что напарсили. Мухи отдельно, котлеты отдельно. А то, потом, у вас в парнсинг и бизнес логика влезет. А потом "черт ногу сломит".
Во вторых - вынес бы парсинг в отдельную функцию. Которая ничерта не знает "откуда взялась строка" из ether-нета или от телепатов. Так и тестить и на другое железо легче перейти будет (и другим, кстати легче будет ваш скетч пускать, даже тем у кого нет вашего шилда).
Вообщем примерно так:
Выведет:
Leshak - просто нет слов! Красиво, черт побери!
И идея красивая (про "," и ":"). И структурно (и в программе у меня) правильнее вынести парсинг и вывод в отдельные процедуры. Я вот тоже хотел разделить, но не нашел красивого метода, как хранить реультат парсинга. Оригинально - со структурой получилось.
Я вот и хотел у зубров такого вот плана совет.
Но больше всего меня поражает, что вы нашли время набрать и проверить текст программы. Еще и с комментариями! Спасибо огромное!
Я снимаю шляпу. Будете в Омске - пожалуйста откликнитесь- надеюсь передать поклон лично.
p.s. Еще про atoi - уж больно много она памяти жрет. Я наверное свой примитивный конвертор оставлю.
>но не нашел красивого метода, как хранить реультат парсинга
Ну вообще структуры - довольно мутная штука. Пару раз наступал на глюки с ними в ардуине. Но даже не зная их, можно было просто объявить два массива int ports[MAX_PARAMS] и int values[MAX_PARAMS] .
>Будете в Омске - пожалуйста откликнитесь- надеюсь передать поклон лично.
"Интернет открывает весь мир и запирает в одной комнате" :) Мне и на маршрутке проехать 5-ть остановок - уже событие жизни :)
>Но больше всего меня поражает, что вы нашли время набрать и проверить текст программы
На что только не пойдешь лишь бы свою основную работу не делать :)
>Еще про atoi - уж больно много она памяти жрет
Ну а что мешает написать свой ее вариант. Более урощенный? Тем более что принцип его построения - вы сами догадались в "своем парсере". Осталось только вынести его в отдельную функцию что-бы не дублировать код при парсинге порта и значения. Да и привязываться к "сколько цифр ожидается" - не сдорово. Лучше универсальную. Вообщем вот:
Разница с прошлым скетчем:
1. atoi заменен на parseByte()
2. В структуре int-ты заменил на byte (я так понял больших чисел у нас нет), а память экономить вы хотите :)
3. Вынес чем разделяются параметры и значение в дефайны (первые две строки). Что-бы потом легче было применить для парсинга нормальных урлов вида htt p://domain.com/?param1=value1¶m2=value¶m3=value . Заменил в дефане запяты и двоеточия на равно и амперсанд - и не нужно по коду лазить.
Найденные мной ошибки:
Cтрокa 39 должна выглядеть так:
Первый раз в 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 - уже избыточен. И то же самое можно сделать так:
Сделал, как писал в пердыдущем посте: выделил разбор на пары "Порт:значение" и разбор внутри пары. Наступил на грабли, как и в прошлый раз: разрушается строка в подпрограмме parsePortValue.
На выводе при строке "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 - уже избыточен. И то же самое можно сделать так:
Leshak, у меня не работало в исходном варианте - выдавало сразу 0 и не разбирало строку. После сделанной мной замены программа заработала как нужно.
Про ошибку логическую - понял. В принципе согласен. Но ловить то ошибку нужно? А то можно вместо света в ванной комнате отопление выключить 8). И узнать об этом только завтра.
Я попробую сам, ладно? А потом критикнете мои потуги..
Таки разрушает, негодяй. Заплевывает ее нулевыми символами.
Вот, возможно вот это поможет втыкнуть "что он творит".
Функция dumpString - выводит строку, нулевой символ показывается звездочкой, и в следующей строке рисует где у нас сейчас указатель который вернул strkok
Выводит
Можете попробовать поэксперементировать с разными строками и вызовами strok и смотреть "что сейчас у нас в строке" и куда счас указатель указывает :) (соответсвенно дальнейший парсинг - будет идти правее него).
Leshak, у меня не работало в исходном варианте - выдавало сразу 0 и не разбирало строку. После сделанной мной замены программа заработала как нужно.
не верю. чудес не бывает. значит вы внесли еще какие-то изменения в код из #24. Я вот взял его, из форума, не откуда-то из своего файла. Скопировал и запустил, не меняя ни одной буквы - работает.
> не работало в исходном варианте
Вы так и не ответили. Насколько этот вариант "исходный"? Вы руками его набирали или копировали через кнопку в верхнем правом углу кода?
>не работало в исходном варианте
Ну вот смотрите, я взял код из #24 и выкинул структуры, массивы, парсинг чисел т.п. просто разбиваю на токены и сразу вывожу обыкновенным Serial.print, что-бы видеть "как строку разбило".
Выводит оно такое:
Теперь вносим ваше "исправление"
Выводит:
Вотзьмите эти два скетча. Пустите у себя и запостите тут что выводит первый и что выводит второй
А подскажите пожалуйста. Я работаю с JSM и принимаю строку
Мне из этой строки нужно изьять номер телефона.
Вот как я пробую делать
В самом конце строчки, которыми я пробую разбить строку.
Чего уже только не пробовал делать, а хочеться чтобы было красиво и этот номер можно было сравнить с заведомо забитым в ардуину номером.
подыму тему
написал функцию парсера, работает хорошо хотя и написано не по феншую...
String str_serial = "action=1&user_id=100&act=55&id=0.";
char _rele1[] = "action"; // название
может каму пригодится,
закидываем строку с параметрами и значениями, и один параметр после которого значение надо считать, получаем то что идетпосле параметра в виде int или -1 если нет такого параметра
Я что то в исходной строке не вижу rele1 или я что то не понял?
Наверное строка 3 должна выглядеть как-то так:
Или что к чему?
Я что то в исходной строке не вижу rele1 или я что то не понял?
Наверное строка 3 должна выглядеть как-то так:
Или что к чему?
Но работает же ))
спасибо, Вы правы, я все поправил.