Zer0Galaxy 2 187 Опубликовано: 27 июня, 2014 (изменено) - Я умею читать чужие мысли! - А я умею читать чужие регулярные выражения! - Ты победил Люди, которые программируют приложения, связанные с обменом по сети rednet или сохранением на диск, часто сталкиваются с необходимостью преобразования массива данных различных типов в строку и последующим его восстановлением. Под массивом я подразумеваю не отдельно взятую таблицу, а несколько переменных, которые нужно упаковать в одну строку. Самый простой но не самый лучший способ решить эту задачу - поместить все необходимые данные в таблицу и воспользоваться функциями serialize, unserialize из библиотеки textutilse. О недостатках такого способа я уже когда-то говорил и сегодня повторяться не буду. А расскажу о том, как эту проблему решаю я. Предположим нам необходимо упаковать в одну строку значения нескольких переменных (x, y, z). Причем строка должна содержать не только значения переменных, но и информацию о том, что это за переменные. Думаю с упаковкой ни у кого больших проблем не возникнет. Сделать это можно, к примеру, так: s='x='..x..' y='..y..' z='..z В результате мы получим строку, которая содержит имена наших переменных и их значения, отделенные от имен знаками равенства. 'x=10 y=11 z=12' Вроде просто. Но как из этой строки извлечь заложенную в нее информацию, а именно имена и значения? Конечно, можно написать большой и сложный алгоритм посимвольного разбора строки. К счастью, вся скучная работа уже сделана за нас и поставленную задачу мы можем выполнить буквально в одно действие. Я говорю о функциях match и gmatch из библиотеки string. Назначение этих функций - поиск шаблона в строке. К примеру, в строке s, которую я привел выше, я хочу найти подстроку 'y=11'. Для этого я вызываю функцию s:match('y=11') которая и вернет мне искомую подстроку, если она там есть. Параметр 'y=11' будем называть шаблоном. Если шаблон в строке не найден, функция вернет nil. Правда здорово? Изменено 29 марта, 2016 пользователем Zer0Galaxy 6 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 27 июня, 2014 Вы наверняка подумали, а зачем искать в строке значение переменной y, если для поиска нужно знать значение этой переменной? Как быть если значение y заранее не известно? Чаще всего так и происходит. Тогда в шаблон вместо цифр необходимо подставить специальные символы. В нашем случае это %d. s:match('y=%d%d') Спецсимволы в шаблоне заменяют собой другие символы. Спецсимволов припоминаю несколько: %a - заменяют все буквы %d - все цифры %w - буквы и цифры %p - знаки препинания %s - пробел .(точка) - любой символ %. - символ "точка" Спецсимволов чуть больше, но остальные не столь актуальны. Надеюсь, понятно, что функция s:match('y=%d%d') будет искать в строке s подстроку состоящую из символов 'y=' и еще каких то двух цифр. Именно эту подстроку она и вернет. Если найдет. А вдруг мы не знаем из скольки символов состоит значение переменной y. В этом случае на помощь приходят модификаторы. Модификаторы это такие спецсимволы, которые изменяют количество символа за которым стоят в шаблоне. Вот такие я знаю модификаторы: + - соответствует одному или более повторений символа; * - соответствует нулю или более повторений; - - соответствует нулю или более повторений. В отличии от модификатора * возвращает строку минимально возможной длины, но так, что бы она соответствовала шаблону; ? - соответствует нулю или одному символу. Если мы знаем, что значение переменной y состоит из какого то количества цифр (но не менее одной), следует применить модификатор + s:match('y=%d+') Эта функция будет искать в строке s подстроку состоящую из символов 'y=' и какого то количества цифр. Причем вернет подстроку максимально возможной длины, т.е. 'y=' и все цифры следующие за знаком равенства. Продолжение следует ... 5 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 1 июля, 2014 Символы и спецсимволы в шаблоне можно объединять в наборы (сеты). Для этого несколько символов заключаются в квадратные скобки. Набор замещает собой один из символов, входящих в его состав. Например, вот такой набор [%d%p] замещает цифру или знак препинания. А такой [_%w] - букву, цифру или символ подчеркивания. Порядок символов, в котором они идут в наборе значения не имеет. Теперь мы можем найти в строке значение переменной y даже если оно будет отрицательным или сопровождаться знаком + s:match('y=[%+%-]?%d+') Знаки % перед + и - говорят о том, что это не спецсимволы, а обычные + и - Знак ? после набора указывает на то, что + или - могут отсутствовать. А вот такой шаблон s:match('y=[%+%-%d]%d*%.?%d*') позволит найти число в формате с десятичной точкой. Если набор начинается с символа ^ , то такой набор интерпретируется как "всё кроме указанных символов". 4 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 1 июля, 2014 Шаблоны, которые я рассматривал до сих пор, возвращали строку, требующую дополнительной обработки. В самом деле, если функция match вернула строку 'y=11', то из этой строки необходимо выделить символы, соответствующие значению переменной. Например, вызвать функцию match еще раз. А нельзя ли обойтись одним вызовом? Оказывается можно. Делается это при помощи структуры, называемой захват (capture). Захват оформляется в виде группы символов, заключенных в круглые скобки. Если шаблон содержит захват, то он возвращает не всю совпавшую подстроку, а только захваченное значение, т.е. символы, соответствующие символам захвата. Пример: s:match('y=(%d+)') вернет строку не 'y=11', а '11' Обращаю внимание, match всегда возвращает строку, даже если вы ищите число. Так что от преобразования типа tonumber никуда не деться. Захватов в шаблоне может быть несколько. В этом случае match возвращает столько значений сколько захватов присутствует в шаблоне. Это позволяет искать сразу несколько значений в строке за один раз. Например: s='x=10 y=11 z=12' x,y,z=s:match('x=(%d+)%s*y=(%d+)%s*z=(%d+)') Тут нужно быть внимательным. Во-первых, не забываем про разделители, если они есть (в примере разделителями являются символы пробел - %s). Во-вторых, следует помнить, что шаблон стреляет только тогда, когда совпал целиком. Т.е. если из трех захватов, приведенных в примере, найдены два, а третий не найден, результатом работы функции будет nil и все три переменные x,y,z будут не определены. Поэтому, если вы не уверены, что в исходной строке присутствуют все искомые значения, воспользуйтесь тремя разными поисками. Или же функцией gmatch, о которой я расскажу чуть позже. 5 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
NEO 542 Опубликовано: 1 июля, 2014 попробуй написать свою функцию типа match только быстрее Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 1 июля, 2014 попробуй написать свою функцию типа match только быстрееНа lua вряд ли получится. Match, всё же на джаве писано. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 7 июля, 2014 Уточню перечень спецсимволов, используемых в шаблонах . (точка) - заменяет любой символ. %a --- все буквы. %c --- все управляющие символы. (Символы с кодом от 0 до 31, которые не имеют графического представления) %d --- все цифры. %l --- все строчные буквы. %p --- все знаки препинания. %s --- все пробелы (символы с кодом 9, 10, 12, 13, 32). %u --- все заглавные буквы. %w --- все алфавитно-цифровые символы (буквы и цифры). %x --- все шестнадцатеричные цифры (цифры и буквы a-f, A-F). %z --- символ с кодом 0. Использование заглавной буквы в качестве спецсимвола меняет смысл на противоположный. Например %A - все символы кроме букв, %S - все печатные символы Так же хочу добавить еще два спецсимвола ^ и $. Если шаблон начинается с символа ^ или заканчивается символом $, то искаться такой шаблон будет соответственно в начале или в конце исходной строки. Несколько примеров использования шаблонов: '[%+%-]?%d+' - ищет целое число в десятичном формате '0x%x+' - число в шестнадцатеричном формате s= s:match('^%s*(.*)%s*$') - удаляет из строки начальные и конечные пробелы key, val = s:match('([%a_][%w_]*)%s*=%s*(%S+)') - разбирает конструкцию типа key = val, причем имя key должно содержать буквы, цифры или символ подчеркивания и начинаться с буквы или подчеркивания, а val может содержать любые символы кроме пробела. 4 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 8 июля, 2014 Вернемся к строке s='x=10 y=11 z=12', с которой мы начинали знакомство с шаблонами. Мы видели, что извлечь из нее значения переменных можно так: x,y,z=s:match('x=(%d+)%s*y=(%d+)%s*z=(%d+)')или так: x=s:match('x=(%d+)') y=s:match('y=(%d+)') z=s:match('z=(%d+)')Но как быть, если количество, содержащихся в строке переменных очень большое, скажем, несколько тысяч? Неужели их все придется прописывать вручную? Нельзя ли это как то автоматизировать? Последний пример из предыдущего поста подсказывает, что можно. Мы ведь можем захватывать не только значения, но и имена переменных. А полученные значения размещать в таблице где имя переменной будет именем элемента таблицы. Для простоты будем считать, что имена могут состоять из букв или цифр, а значения только из цифр. Пропишем шаблон для поиска одной переменной и заключим его в цикл для поиска остальных. s='x=10 y=11 z=12' -- исходная строка t={} -- таблица, в которой будем хранить результат for i=1,3 do key, val=s:match('(%w+)=(%d+)') -- извлекаем имя и значение t[key]=tonumber(val) -- помещаем результат в таблицу end for key, val in pairs(t) do -- выводим результат print(key,'=',val) endПриведенный пример кода загружает из исходной строки имена и значения переменной в таблицу t. Однако, если мы попытаемся этот код выполнить, то увидим, что в результате таблица t будет содержать только одно значение x=10. Произошло так потому, что функция match всегда начинает поиск совпадений с начала строки. Всегда, если ей не указать с какого места нужно начинать поиск. Оказывается match имеет еще один необязательный параметр, указывающий с какой позиции строки следует начинать поиск. Его можно узнать вставив в шаблон пустой захват (). Такой захват вернет позицию символа в строке, следующего за найденной подстрокой. s='x=10 y=11 z=12' -- исходная строка t={} -- таблица, в которой будем хранить результат n=1 -- начинаем поиск с начала строки for i=1,3 do key, val, n=s:match('(%w+)=(%d+)()',n) -- извлекаем имя и значение -- пустой захват ставим в конце шаблона, чтобы от вернул позицию символа, следующего за найденной подстрокой t[key]=tonumber(val) -- помещаем результат в таблицу end for key, val in pairs(t) do print(key,'=',val) endПосле внесенных изменений код должен работать более корректно. 2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 8 июля, 2014 Но лично я в подобных случаях не заморачиваюсь с пустыми захватами, а использую функцию gmatch. Она возвращает функцию-итератор, которая при каждом очередном вызове ищет следующее совпадение шаблона в строке. Функция возвращает функцию! Звучит ужасно, но выглядит всё не так страшно, если применить ее в операторе for in s='x=10 y=11 z=12' -- исходная строка t={} -- таблица, в которой будем хранить результат for key, val in s:gmatch('(%w+)=(%d+)') do t[key]=tonumber(val) -- помещаем результат в таблицу end for key, val in pairs(t) do print(key,'=',val) endОператор for in выполнится столько раз, сколько раз будет найден шаблон '(%w+)=(%d+)' в исходной строке. Причем переменные key и val всякий раз будут принимать очередные захваченные значения - имя и значение переменной.Думаю, теперь мы готовы к тому, что бы написать собственные аналоги функций serialize и unserialize. 5 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
DUIIIES 284 Опубликовано: 3 декабря, 2014 Ух, наконец-то я осилил прочтение и понимание этого материала... Спасибо большое, Зеро, написано просто и понятно на столько, на сколько это возможно) Я прям чувствую, как мой лвл апнулся)) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
JaggerDer 90 Опубликовано: 23 декабря, 2014 Кстати, когда я узнал о таких шаблонах наворотил такую шнягу: arg = { ... } local function man(algo) for v in string.gmatch(algo, "%a") do if (v == "l") then turtle.turnLeft() elseif (v == "r") then turtle.turnRight() elseif (v == "f") then turtle.forward() elseif (v == "u") then turtle.up() elseif (v == "d") then turtle.down() elseif (v == "b") then turtle.back() elseif (v == "p") then print("lol") else print("Impossible algorithm.") return false end end return true end man(tostring(arg[1])) Мне такая штука показалась достаточно удобна. Просто задаем алгоритм телодвижений черепашки одной строкой, например fffllffrfffb. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 23 декабря, 2014 Можно задавать количество повторений в виде числа. Скажем так: local function man(algo) for v,d in string.gmatch(algo, "(%a)(%d*)") do d=tonumber(d) or 1 for i=1,d do if (v == "l") then turtle.turnLeft() elseif (v == "r") then turtle.turnRight() elseif (v == "f") then turtle.forward() elseif (v == "u") then turtle.up() elseif (v == "d") then turtle.down() elseif (v == "b") then turtle.back() elseif (v == "p") then print("lol") else print("Impossible algorithm.") return false end end end return true end Тогда алгоритм будет выглядеть как-то так "f30u15rf10". 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Jet 30 Опубликовано: 23 декабря, 2014 Я использую подобную фишку в своих боевых черепашках, но не на выполнение. Скажите, а как прервать такой алгоритм? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 23 декабря, 2014 Ctrl+T 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Jet 30 Опубликовано: 23 декабря, 2014 Ну Ctrl+T можно что угодно прервать (кроме os.pullEventRaw(), если terminate не обработан). Ctrl+T это очень не гибко. Еще варинаты есть? Кроме флагов, типа while run do или if run then. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy Автор темы 2 187 Опубликовано: 24 декабря, 2014 Хочешь гибкости? Тогда нужно решить по какому событию прерывать алгоритм (и нужно ли вообще). Вот это событие и отлавливать. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Jet 30 Опубликовано: 24 декабря, 2014 Хочешь гибкости? Тогда нужно решить по какому событию прерывать алгоритм (и нужно ли вообще). Вот это событие и отлавливать. Я сделал таблицу функций, которая является очередью, и запихал туда turtle.forward() и т.д., в отдельном потоке выполняю эту очередь, если надо прервать выполнение - очищаю таблицу. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Asummonster 592 Опубликовано: 11 ноября, 2020 Думаю будет полезно https://gitspartv.github.io/lua-patterns/ 5 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах