реклама на сайте
подробности

 
 
> Поиск команды в массиве данных
interrupt
сообщение Dec 3 2015, 19:32
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 20
Регистрация: 28-02-08
Пользователь №: 35 466



Такая задача. Контроллер принимает по UART некоторые данные в виде строк разной длины и содержания. Т.е. обычные текстовые строки. В этих строках может содержаться команда для контроллера, например: Switсh on out 7, input 1 disable, Led 11 on.
Строки хранятся до разбора в массиве. Размер массива ограничен, а частота поступления новых строк не нормируется. Поэтому нужно быстро разобрать строку, вычленить из нее ключевые слова и передать соответствующие команды основной программе. Задача усложняется тем, что таких слов будет около 100, кроме того, в будущем их кол-во предполагается увеличить.
Как реализуется обычно такая задача? Ничего умнее, как последовательный поиск каждого слова в строке придумать не могу. Но это очень долго получается. Например, сначала придется пролистать весь массив в поисках "Led", потом опять весь массив в поисках "input" и т.д. А хочется читать строку 1 раз, по пути находя все ключевые слова.
Go to the top of the page
 
+Quote Post
2 страниц V   1 2 >  
Start new topic
Ответов (1 - 14)
Александр77
сообщение Dec 3 2015, 19:50
Сообщение #2


Знающий
****

Группа: Свой
Сообщений: 608
Регистрация: 10-07-09
Из: Дубна, Московская область
Пользователь №: 51 111



Если есть возможность повлиять на формирование самих команд, то можно перед "значимой" частью передавать преамбулу (уникальный символ или группа символов). Найдя преамбулу в строке можно взвести флаг о том, что дальше идет/идут команды. Окончание поля команд тоже можно обособить для того, чтобы декодировку и поиск.
Go to the top of the page
 
+Quote Post
interrupt
сообщение Dec 3 2015, 20:10
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 20
Регистрация: 28-02-08
Пользователь №: 35 466



Да в общем-то все данные получаются значимыми. Просто не известно, какая команда будет первой, какая последней, сколько команд будет в строке и каких именно. Т.е. строка может выглядеть так:

Switch on out 1, led 7 off.

А может так:

led 8 on, timer1 stop, Switch on out6, led 9 off

Как пример.
Есть GSM-модуль, управляемый через терминал. Допустим, я ввожу команду AT+CGSN\r\n
Что происходит дальше? Как контроллер обрабатывает эту строку и находит соответствие этой записи конкретной команде? Вот мне нужно реализовать примерно то же самое.
Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Dec 3 2015, 20:55
Сообщение #4


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(interrupt @ Dec 3 2015, 20:10) *
Да в общем-то все данные получаются значимыми. Просто не известно, какая команда будет первой, какая последней, сколько команд будет в строке и каких именно. Т.е. строка может выглядеть так:

Switch on out 1, led 7 off.

led 8 on, timer1 stop, Switch on out6, led 9 off


Ну, для начала надо взять большой листок бумаги и разрисовать машину состояний, которая будет обрабатывать ваши ключевые слова.
Например, команда "Switch on out 1" правильная, а "Switch 1 on out" неправильная, я надеюсь. sm.gif То есть при приеме токена "switch" следующим токеном должен быть "on" или "off" итп.
Если вы начнете кодить не имея полного формального описания автомата разбора, ничего не получится.

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

А вот потом начнется самое интересное sm.gif Тестирование sm.gif А то в ваш парзер придет строка "led -1 fuck off" и он зависнет в бесконечном цикле sm.gif

Сообщение отредактировал CrimsonPig - Dec 3 2015, 20:58
Go to the top of the page
 
+Quote Post
interrupt
сообщение Dec 3 2015, 21:11
Сообщение #5


Участник
*

Группа: Участник
Сообщений: 20
Регистрация: 28-02-08
Пользователь №: 35 466



Цитата(CrimsonPig @ Dec 3 2015, 23:55) *
Ну, для начала надо взять большой листок бумаги и разрисовать машину состояний, которая будет обрабатывать ваши ключевые слова.
Например, команда "Switch on out 1" правильная, а "Switch 1 on out" неправильная, я надеюсь. sm.gif То есть при приеме токена "switch" следующим токеном должен быть "on" или "off" итп.
Если вы начнете кодить не имея полного формального описания автомата разбора, ничего не получится.

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

А вот потом начнется самое интересное sm.gif Тестирование sm.gif А то в ваш парзер придет строка "led -1 fuck off" и он зависнет в бесконечном цикле sm.gif


Ну примитивный разбор работает. Т.е. я просматриваю строку в поисках "Switch". Далее смотрю уже "off" или "on", далее объект "out", потом номер.
Далее приходится снова просматривать всю строку в поисках "timer" -> номер таймера -> команда.

Необходимо оптимизировать именно начальный поиск (а оп аналогии и разветвления так же). Ну например, просматриваю строку. Первая буква "S" - таких команд 10. Вторая "w" - таких команд 5. Третья "i" - таких команд 1. Проверяю, действительно ли это она ну и смотрю ее параметры.
Далее нахожу разделитель, например ",". После нее 1-я буква "L" - 20 команд, 2-я "e" - 2 команды, ну и т.п. Т.е. обработка всей строки за 1 проход.
Go to the top of the page
 
+Quote Post
gerber
сообщение Dec 3 2015, 22:19
Сообщение #6


Знающий
****

Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088



Можно составить статический связный список соответствия вида 'символ' -> 'команда1', 'команда2', ... 'командаN', в который внести команды, в которых этот символ встречается, и упорядочить его по частоте употребления символа в командах, начиная с наиболее популярного. Например, в вашем случае словарь команд состоит из 3 команд - switch,timer,led. Видим, что самые популярные символы в командах - это t, i и e (встречаются по 2 раза). Начнём с любого из них, например, t:
Код
't' -> "timer", "", "", "switch", NULL
'e' -> "", "led", NULL

Позиция команды в списке "по горизонтали" соответствует позиции искомого символа внутри команды. NULL заканчивает связный список.

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

Сообщение отредактировал gerber - Dec 3 2015, 22:21


--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
Go to the top of the page
 
+Quote Post
jcxz
сообщение Dec 4 2015, 03:34
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(interrupt @ Dec 4 2015, 01:32) *
Такая задача. Контроллер принимает по UART некоторые данные в виде строк разной длины и содержания. Т.е. обычные текстовые строки. В этих строках может содержаться команда для контроллера, например: Switсh on out 7, input 1 disable, Led 11 on.

У Вас как я понял строка-команда представляет из себя смесь команд (которые являются группой из латинских букв+цифр) разделённых знаками препинания и пробелами?
Тогда разбиваете строку на лексемы (группы алфавитно-цифровых символов разделённых знаками препинания) за один проход по строке.
Потом просто для каждой лексемы ищете её в массиве - есть-ли такая.
Массив - это набор хешей (ну или просто CRC32) для всех возможных команд. Тогда для каждой лексемы достаточно одного прохода поиска по массиву 32-битных слов.
Да и поиск лучше делать по ходу выделения лексем. Так проще контролировать правильность синтаксиса всей строки.
Go to the top of the page
 
+Quote Post
Dog Pawlowa
сообщение Dec 4 2015, 07:00
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823



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


--------------------
Уходя, оставьте свет...
Go to the top of the page
 
+Quote Post
jcxz
сообщение Dec 4 2015, 07:53
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Dog Pawlowa @ Dec 4 2015, 13:00) *
Я бы задумался над постановкой задачи, ибо в ней противоречие. С одной стороны, ввод текста человеком, а с другой, жесткие рамки парсера.

Если ввод человеком, то требования по скорости должны быть вообще никакие. А тут какие-то массивы на множество строк, непонятно зачем.....
Go to the top of the page
 
+Quote Post
psL
сообщение Dec 4 2015, 09:18
Сообщение #10


Знающий
****

Группа: Свой
Сообщений: 526
Регистрация: 5-08-05
Пользователь №: 7 390



вот проект. решает аналогичную задачу. может пригодится.
Прикрепленные файлы
Прикрепленный файл  lwshell.zip ( 6.23 килобайт ) Кол-во скачиваний: 30
 
Go to the top of the page
 
+Quote Post
interrupt
сообщение Dec 4 2015, 09:30
Сообщение #11


Участник
*

Группа: Участник
Сообщений: 20
Регистрация: 28-02-08
Пользователь №: 35 466



Ввод может быть как человеком, так и внешним устр-вом. Как пример я привел GSM-модуль: можно ручками через терминал писать ему команды, а можно выстреливать их из контроллера. При этом последовательность команд будет разной, кол-во в общем-то тоже (0xOD выступает в качестве разделителя и флага одновременно).
C CRC32 интересно, тоже думал в этом направлении. Наверно, это наиболее быстрый вариант (считать CRC32 для первого слова, искать в массиве команд. Потом CRC второго слова - искать в массиве, и так, пока не наткнулся на подходящее. Разобрал команду - пошел дальше).
Go to the top of the page
 
+Quote Post
Lmx2315
сообщение Dec 4 2015, 09:37
Сообщение #12


отэц
*****

Группа: Свой
Сообщений: 1 729
Регистрация: 18-09-05
Из: Москва
Пользователь №: 8 684



Может упростить задачу?
Писать примерно так : "~ включить_канал:1;"
где "~" - стартовый байт
"включить_канал" - команда
":" признак что потом идёт число
";" признак конца пачки
команду собирать побайтно в массив и сравнивать через strcmp .
IO("~ включить_канал:1;",19);
Прикрепленные файлы
Прикрепленный файл  io.txt ( 7.5 килобайт ) Кол-во скачиваний: 21
 


--------------------
b4edbc0f854dda469460aa1aa a5ba2bd36cbe9d4bc8f92179f 8f3fec5d9da7f0
SHA-256
Go to the top of the page
 
+Quote Post
interrupt
сообщение Dec 4 2015, 09:54
Сообщение #13


Участник
*

Группа: Участник
Сообщений: 20
Регистрация: 28-02-08
Пользователь №: 35 466



Цитата(psL @ Dec 4 2015, 12:18) *
вот проект. решает аналогичную задачу. может пригодится.


Спасибо, пойду разбираться.
Go to the top of the page
 
+Quote Post
XVR
сообщение Dec 4 2015, 10:24
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Вам подойдет поиск по Регулярным Выражениям. Из всех ваших команд делается 1 регулярное выражение, из которого строится конечный автомат. Он (автомат) запускается на входной поток символов и выдает все совпадения за 1 проход над входной строкой (на лету)

Если вас устроит готовый парсер можете посмотреть в сторону flex - он генерирует готовый автомат
Go to the top of the page
 
+Quote Post
interrupt
сообщение Dec 4 2015, 10:25
Сообщение #15


Участник
*

Группа: Участник
Сообщений: 20
Регистрация: 28-02-08
Пользователь №: 35 466



Цитата(Lmx2315 @ Dec 4 2015, 12:37) *
Может упростить задачу?
Писать примерно так : "~ включить_канал:1;"
где "~" - стартовый байт
"включить_канал" - команда
":" признак что потом идёт число
";" признак конца пачки
команду собирать побайтно в массив и сравнивать через strcmp .
IO("~ включить_канал:1;",19);


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

За пример спасибо.
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 07:54
Рейтинг@Mail.ru


Страница сгенерированна за 0.01498 секунд с 7
ELECTRONIX ©2004-2016