Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

MyDC.ru _ Программирование на Lua _ Несколько Слов О Захватах И Регулярных Выражениях

Автор: Setuper 7.8.2008, 15:54

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

Сразу же начну с нескольких примеров:

Код
local s, e, a, b, c, d = string.find("11.12.13.14", "^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
local _, _, a, b, c, d = string.find("11.12.13.14 ", "^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
local _, _, a, b, c, d = string.find("10 11.12.13.14 15", "(%d+)%.(%d+)%.(%d+)%.(%d+)")
local _, _, a = string.find("Hello world", "%S+%s(%S+)")
local _, _, a, b, c = string.find("<User> !massmsg hello world", "%b<>%s+(%S)(%S+)%s*(.*)")


Можете ли вы сказать, что тут выполняется??? Если не можете, то слушайте дальше...

Сначала расскажу, что такое захват, и что такое регулярное выражение.
Формально, захват - это нахождение определённый последовательности символов и помещение их в переменную.
Например, возьмём один из примеров, который я написал сверху:
Код
local s, e, a, b, c, d = string.find("11.12.13.14", "^(%d+)%.(%d+)%.(%d+)%.(%d+)$")

этот пример - типичный пример захвата. Функция string.find "захватывает" из строки "11.12.13.14" определённую последовательность символов.
Выражение "^(%d+)%.(%d+)%.(%d+)%.(%d+)$", называется РЕГУЛЯРНЫМ ВЫРАЖЕНИЕМ.
в скобках указываются захваты, то есть тут 4 захвата. Из этой функции данные у нас заносятся в 6 локальных переменных, а именно s, e, a, b, c, d.
В переменную s (s от слова start) заносится начальная позиция захвата, равная 1. У нас строка "11.12.13.14" имеет 11 символов, следовательно 11 позиций. В переменную e (e от слова end) заносится конечная позиция захвата, равная 11. Обычно, в скриптах эти позиции не нужны, тогда, обычно, переменные обозначают одним именем (для сохранения памяти), обычно, обозначают как "_" (чтобы эта переменная была не столь заметна).
Далее в переменные a, b, c, d, соответственно, заносятся захваты (то, что захватывается скобками).
Разберём регулярное выражение: "^(%d+)%.(%d+)%.(%d+)%.(%d+)$".
Символ ^ означает, что следующее действие в регулярном выражении должно быть в начале строки. У нас следующее за символом ^ действие - это захват (%d+), следовательно, захват у нас должен выполняться обязательно с начала строки. Сейчас объясню более понятно на примерах.
Возьмём два регулярных выражения "^(%d+)" и "(%d+)".
Теперь с помощью действий:
Код
local s, e, a1 = string.find("11.12.13.14", "^(%d+)")
local s, e, a2 = string.find("11.12.13.14", "(%d+)")

в переменные a1 и a2 запишутся данные: a1="11" и a2="11".
Теперь изменим исходную строку (дописав вначале строки пробел):
Код
local s, e, a1 = string.find(" 11.12.13.14", "^(%d+)")
local s, e, a2 = string.find(" 11.12.13.14", "(%d+)")

смотрим, что в переменных: a1=nil и a2="11".
Теперь я думаю вам почти понятно в чем вся фишка, осталось только объяснить, что такое (%d+).
(%d+) - это захват числа. Во второй строке вначале стоял пробел, поэтому выражение ^(%d+) не могло захватить число вначале строки, поэтому и функция string.find вернула nil, и записала в переменные s=nil, e=nil, a1=nil, а функция string.find(" 11.12.13.14", "(%d+)") записала в переменные s=1, e=2, a2="11". Заметьте, что хоть мы и захватываем число, но тем не менее оно является строковым числом "11", а не просто 11 !!!!!!!!.
Аналогично дело обстоит с символом $, но только для конца строки.
Таким образом, мы рассмотрели 3 первых примера, которые я предложил вначале.
В первом примере: s=1, e=11, a="11", b="12", c="13", d="14" и захват происходит именно 4 чисел, разделённых точками, и никакой другой комбинации
Во втором: _=nil, a=nil, b=nil, c=nil, d=nil и захвата не произошло из-за пробела в конце.
В третьем: _=14, a="11", b="12", c="13", d="14" захват 4 чисел, разделённых точками, в любом месте (хоть вначале строки, хоть в середине, хоть в конце), и строка не обязана содержать только 4 числа, разделённых точками, в отличие от первых двух примеров.

Идём далее. Выпишу основные регулярные выражения:

Цитата
. - любой символ
%a - латинская буква
%с - контрольный символ
%d - десятичная цифра
%u - латинская буква верхнего регистра
%l - латинская буква нижнего регистра
%p - знак пунктуации
%s - символ пробела
%w - латинская буква или арабская цифра
%z - нулевой символ

%A - не латинская буква
%C - не контрольный символ
%D - не десятичныая цифра
%U - не латинская буква верхнего регистра
%L - не латинская буква нижнего регистра
%P - не знак пунктуации
%S - не символ пробела
%W - не латинская буква и не арабская цифра
%Z - не нулевой символ

вроде становится понятнее, не так ли?

Эквиваленты:
%d эквивалентно [0-9]
%D эквивалентно [^%d]
%w эквивалентно [A-Za-z0-9]
%W эквивалентно [^%w]

в 4 и 5 примерах встречается регулярное выражение %S. Находим в списке выше, и видим: %S - не символ пробела. То есть, это означает, что это не пробел, а следовательно это любой символ, кроме пробела.

Теперь рассмотрим всякие плюсики. Я буду рассматривать их совместно с регулярным выражением %s, то есть с символом пробела.

Цитата
%s - 1 символ пробела
%s+ - 1 или более символов пробела
%s- - 0 или более символов пробела
%s* - 0 или более символов пробела
%s? - 0 или 1 символ пробела


Теперь объясню разницу между * и -
Удобнее всего на следующем примере. Я уже рассказал, что означает точка (смотри выше в списке регулярных выражений: "." - любой символ).
Рассмотрим примеры следующих регулярных выражений:
Цитата
"(.*)" - захват всей строки
"/(.*)/" - захват всего, что находится между КРАЙНИМИ символами /
"/(.-)/" - захват всего, что находится между ПЕРВЫМИ ДВУМЯ символами /


Цитата
%bxy - нахождение, а в случае захвата и захват, всего того, что находится между символами x и y, включая концы x и y


Ну и последнее...

А если, допустим, я хочу захватить точку. Как быть?
Дело в том, что точка является так называемым магическим символом.
Магические символы: ( ) . % + - * ? [ ] ^ $
Перед всеми магическими символами нужно ставить %, то есть все магические символы надо экранировать.
Таким образом, чтобы захватить точку надо написать "(%.)"


На последок ещё несколько часто используемых регулярных выражений:

Цитата
"(%S)" - захват первого не пробельного символа строки;
"(%S*)" - захват первого слова или пустого слова "";
"%S*%s*(%S*)" - захват второго слова;
"(%S*)%s*(%S*)" - захват первого и второго слова;
"(%S%S)" - захват первых двух не пробельных символов;
"(%S+)%s*(%d*)" - захват слова и числа;
"(%d*%.%d*%.%d*%.%d*)" - захват строки из 4 чисел, разделенных точками (например ip адреса);
"%s*(%d*%.%d*%.%d*%.%d*)%s*(%d*)%s*(.*)" - захват чисел, разделенных точками, захват числа, захват всего оставшегося;
"()" - захват номера позиции;
"(why)" - захват слова why (в любом месте);
"^(why)" - захват слова why, только если оно стоит на первых трех (в данном случае) позициях;
"(why)$" - захват слова why, только если оно стоит на последних трех (в данном случае) позициях;
"%b<>%s+(%S)(%S+)%s*(.*)" - захват первого символа, сразу после текста, ограниченного символами < >, второй захват слова сразу после захваченного символа, третий захват - захват всего оставшегося.
[^/]+ - не пустой захват с начала до символа /


Теперь, я думаю, что вы и сами сможете написать, что же получается в примерах 4 и 5???

Автор: district 10.11.2008, 9:34

Уф, суще дельный и юсфульный топик, заслуживает самого пристального изучения, исключительно на трезвую голову и после хорошего здорового сна big_smile.gif
Спасибо big_smile.gif

Автор: Setuper 6.8.2009, 11:19

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

Речь идёт о методе sub (или функции string.sub). Данный метод (функция) служит для обрезания строк или для выделения подстрок.

Код
sData = sData:sub(s, e)

В переменную sData будет возвращена подстрока строки sData начиная с позиции s и заканчивая позицией e. s и e могут быть также отрицательными. В случае когда они положительные, всё понятно, например sData:sub(3, 6). Тут этот метод возьмёт из sData с третьего до шестого символа включительно. Если же sData:sub(-5, -2), то метод возьмёт из sData со второго до пятого символа включительно, но уже отсчёт позиций будет идти с конца строки. В этой функции последний параметр является необязательным, т.е. можно написать sData:sub(2), в этом случае последний параметр берётся по умолчанию -1. Если последний параметр равен -1 - это означает, что строка берётся до конца, в данном примере со второго символа и до конца.

Пример строки:
Код
<НИК> Пример.|

Составим таблицу для большей наглядности:
Код
'символ', 'позиция от начала', 'позиция от конца'
'<',    '1',    '-14'
'Н',    '2',    '-13'
'И',    '3',    '-12'
'К',    '4',    '-11'
'>',    '5',    '-10'
'пробел',    '6',    '-9'
'П',    '7',    '-8'
'р',    '8',    '-7'
'и',    '9',    '-6'
'м',    '10',    '-5'
'е',    '11',    '-4'
'р',    '12',    '-3'
'.',    '13',    '-2'
'|',    '14',    '-1'


Последний символ | мы не видим, но он есть, этот символ не выводится в чат, но позволяет скрипту определять конец строки, поэтому в случае использования кода sData:sub(1, -2) как раз таки мы избавляемся от этого символа, т.е. берём строку начиная с первого символа и, заканчивая вторым с конца, так как первый с конца это символ |.

А вообще, применяют чаще всего этот метод чтобы отсечь ник и символ | и оставить только данные, посылаемые в чат, вот так:
Код
sData = sData:sub(tUser.sNick:len() + 4, -2)

Автор: Nickolya 10.8.2009, 10:56

Мерси, а методы len и sub естественно быстрее чем match или find ?!
Т.е. к примеру, берем строку "<Nick> lalalalala|", в ней нам нужен текст сообщения, ник нам известен, вот примерный код теста:

CODE
sData = "<Nick> lalalalala|"
sNick = "Nick"

iBegin = os.clock()
for i = 1, 1000000 do
--sNeedData = sData:match("^%b<>%s(.*)|$")
sNeedData = sData:sub(sNick:len() + 4, -2)
end

--Core.SendToAll((os.clock() - iBegin).." secs with _match_ and data: "..sNeedData)
Core.SendToAll((os.clock() - iBegin).." secs with sub and data: "..sNeedData)


А вот результат, сначала закоментированы 2е строки, 5 раз, потом первые:
Цитата
[11:52:22] 0.74899999999991 secs with _match_ and data: lalalalala
[11:52:23] 0.71699999999998 secs with _match_ and data: lalalalala
[11:52:25] 0.73300000000006 secs with _match_ and data: lalalalala
[11:52:25] 0.73299999999995 secs with _match_ and data: lalalalala
[11:52:26] 0.74900000000002 secs with _match_ and data: lalalalala
[11:52:41] 0.702 secs with sub and data: lalalalala
[11:52:42] 0.71799999999996 secs with sub and data: lalalalala
[11:52:44] 0.702 secs with sub and data: lalalalala
[11:52:44] 0.74900000000002 secs with sub and data: lalalalala
[11:52:44] 0.73299999999995 secs with sub and data: lalalalala

Как видите на милионе операций разница есть, незначительная, так что лучше использовать?

Автор: Setuper 10.8.2009, 11:24

Быстродействие практически одинаковое. Использование match должно быть по идее эффективнее прежде всего из-за того, что вызывается всего 1 функция. Подобного рода тесты не могут точно определить быстроту выполнения, так как довольно большая погрешность вносится побочными процессами. Поэтому подобное сравнение ни о чём не говорит.

Автор: red_neon 16.10.2009, 23:47

Ещё стоит добавить, что луа не поддерживает PCRE/PREG
т.е. регэкспа типа "^.+(%.mp[1-3]$|%.wav$|%.flac$)" работать не будет.

Автор: Setuper 16.10.2009, 23:51

Действительно, lua использует урезанную версию регулярных выражений.
Однако, предложенное регулярное выражение может быть преобразовано и будет работать))

Автор: Кто-то_из_вне... 28.10.2009, 15:43

м...много материала , но в целом все доступно и понятно

Автор: dimajak 19.11.2010, 2:45

как понять это?

Цитата
"/(.-)/" - захват всего, что находится между ПЕРВЫМИ ДВУМЯ символами /
трудности перевода? Или как-то иначе?
Первые два символа "/" - это "//" в начале строки. Что может быть между ними?

Автор: Setuper 19.11.2010, 9:54

Не говорится же, что эти символы обязательно следуют друг за другом.
Если бы имелись ввиду первый два символа строки, то так и было бы написано: "первые два символа СТРОКИ", а тут написано "ПЕРВЫМИ ДВУМЯ символами /", и про расположение этих символов в строке ничего не сказано.

Символов в строке может быть больше двух. Мы получаем всё то, что находится между первыми двумя символами /.

Код
"qwerty/12345/6789/00000///123"  -> "12345"

Автор: Ksan 19.11.2010, 11:56

Setuper, это относится ТОЛЬКО к символу слэш '/' ?
Для всего остального (захват МЕЖДУ символами):

Цитата
%bxy
?

Автор: Setuper 19.11.2010, 12:16

Можно и то и другое использовать, только в случае %bxy захватываются ещё и концы x и y.
То есть %b// захватить всё между первыми двумя символами /, включая и сами символы.

Вообще говоря примеры:

Цитата
"/(.*)/" - захват всего, что находится между КРАЙНИМИ символами /
"/(.-)/" - захват всего, что находится между ПЕРВЫМИ ДВУМЯ символами /
были написаны для того, чтобы показать разницу между регулярными выражениями .* и .- , а не для того чтобы как-то обыграть слеш big_smile.gif

Автор: Ksan 19.11.2010, 13:01

Кстати, а разницу-то я между '*' и '-' так и не понял из объяснения..

Автор: Setuper 19.11.2010, 13:26

Может на реальном примере поймёшь, раз на словах не понимаешь:

Код
local s = "123/456/789/0"
print(s:match"/(.*)/") --> "456/789"
print(s:match"/(.-)/") --> "456"

Автор: dimajak 20.11.2010, 3:06

Setuper, теперь ясно, спасибо. Дополните первое сообщение приведенными примерами.

Хочу сделать приватные сообщения в общем чате, т.е. обрабатывать сообщения вида
"user1, user2,user3: Текст сообщения"
"user1: Текст сообщения"
"user1, user2, user3: Текст сообщения big_smile.gif"
и т.д.
и отправлять их юзеру либо юзерам перечисленным до ":".
Пока моих знаний хватило на это:

Код
ChatArrival = function(user,sData)
    local _, _, nicks, msg = string.find(sData, "%b<>%s+(.*):%s*(.*)")
...
Далее nicks парсится и проверяется на ники юзеров. Если какое-либо слово из nicks не является ником юзера, то не происходит отправки сообщения указанным юзерам. (nicks - строка из одного, либо нескольких слов, разделенных ",", может быть наличие пробелов между словами и знаками).
"%b<>%s+(.*):%s*(.*)" - этого достаточно?
Важно учесть смайлики и правила русского языка в применении знака ":", т.е. после него мб перечисление и пр., т.е. попросту проверить на наличие ников в части до символа ":", но это проверяется потом парсингом nicks на наличие ников, главное чтобы захватить строку до первого ":".

Автор: Ksan 20.11.2010, 6:40

Попробуй вот этот корявый код:

Код
local tShadowChatNicks = {  -- эти ники могут писать мультиадресные сообщения
    ["Indy"] = 1,
    ["Ksan"] = 1,
    ["Атыктотакой"] = 1,
}
local tTable = {}

function ChatArrival(tUser, sData)
    local sData = string.sub(sData, 1, -2)
    local sMsg = sData:match("^%b<>%s+(%S.*)")
    if sMsg and sMsg:find":" then
        if tShadowChatNicks[tUser.sNick] then
            local sNicks, sShadowMsg = sMsg:match"^(.*):%s*(.*)"
            sNicks = sNicks:gsub(",","")
            while sNicks do
                sWord = sNicks:match"^(%S+)"
                table.insert(tTable, sWord)
                sNicks = sNicks:gsub(sWord, "")
                sNicks = sNicks:match"^%s+(.*)"
            end
            for i = 1, #tTable do
                Core.SendToNick(tTable[i], "<"..tUser.sNick.."> "..sShadowMsg)  -- тут сообщение отправляется очередному нику
            end
            Core.SendToNick(tUser.sNick, sData)  -- а это идёт себе же для контроля
            tTable = {}  -- обнуляем таблицу, готовя к след. сообщению
            collectgarbage()
            return true
        end
    end
end

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

Добавил ещё сверку ника автора сообщения со списком имеющих право на такие сообщения.

P.S.: Можно еще сделать так, чтоб каждому адресату при этом сообщалось, кому ещё это сообщение отправлено.
P.P.S: Только нужно помнить, что это сообщение отправится на запись в чат-лог, поэтому скрипт нужно поставить выше Чат-лога.

Автор: dimajak 24.11.2010, 23:51

Ksan, несколько иначе "выковыривал" ники, а так почти 1 в 1 big_smile.gif
пришлось добавить аналог PHP-функций http://mydc.ru/r/?http://www.php.su/functions/?explode и http://mydc.ru/r/?http://www.php.su/functions/?trim.

Хотелось бы, конечно увидеть мнение гуру Setuper.

P.S.

Цитата(Ksan @ 20.11.2010, 6:40) *
Core.SendToNick(tUser.sNick, sData) -- а это идёт себе же для контроля
сильное замечание - я почти сутки дебажил код чтобы увидеть то что отправил big_smile.gif

Автор: Setuper 25.11.2010, 11:45

Цитата
"%b<>%s+(.*):%s*(.*)" - этого достаточно?
Важно учесть смайлики и правила русского языка в применении знака ":", т.е. после него мб перечисление и пр., т.е. попросту проверить на наличие ников в части до символа ":", но это проверяется потом парсингом nicks на наличие ников, главное чтобы захватить строку до первого ":".


Для кого я писал различия между ' * ' и ' - ' ?

Код
function ChatArrival(user, sData)
  local nicks, msg = sData:match"%b<>%s+(.-)%s*:%s*(.*)"
...

Автор: Sekretchik 7.1.2011, 1:04

Доброго вам всем времени суток. Подскажите пожалуйста мне такой момент. Пытаюсь сделать захват строки MyINFO. Но получается 5 переменных

Код
local _, _, name, desc, speed, email, share = string.find(tUser.sMyINFO, "$MyINFO $ALL (%S+)%s+([^$]*)$ $([^$]*)$([^$]*)$([^$]+)")

Я понимаю, что строка целиком выглядит так (Спасибо - Setuper - http://mydc.ru/index.html?showtopic=915&view=findpost&p=6721):
Код
$MyINFO $ALL [Ник] [Описание][Тэг]$ $[Соедиенние][Флаг]$[E-Mail]$[Шара]$|

соответственно, захват из моего примера берёт:
$MyINFO $ALL (%S+)%s+ равно $MyINFO $ALL [Ник]+пробел
([^$]*)$ равно [Описание][Тэг]$ - одной строкой до знака - $
$([^$]*)$([^$]*)$([^$]+) равно $[Соедиенние][Флаг] - одной строкой до знака - $ затем $[E-Mail]$[Шара]$|

Подскажите, как лучше сделать захват по всем значениям MyINFO - что бы захватило все значения в 7 переменных.
Код
$MyINFO $ALL [Ник] [Описание][Тэг]$ $[Соедиенние][Флаг]$[E-Mail]$[Шара]$|

Голова кругом пошла...
Буду признателен за совет.

Автор: Setuper 7.1.2011, 1:48

Не знаю зачем парсить это руками, когда обычно хаб парсит это дело, однако вот решение:

Код
local sNick, sDesc, sSpeed, sEmail, iShare = tUser.sMyINFO:match"%$MyINFO %$ALL (%S+)%s+(.-)%$ %$(.-)%$(.-)%$(%d+)%$"

Автор: Sekretchik 7.1.2011, 2:20

Спасибо Вам Илья. Я учусь и набираю опыт с вашей помощью.
Но у меня вопрос. в переменную sDesc записывается [Описание][Тэг]. Я хотел бы разделить их на две разные переменные. Это возможно?

Автор: Setuper 7.1.2011, 12:21

Код
local sDescription, sTag = sDesc:match'^(.*)(<.*>)$'
if not sDescription then
  sDescription = sDesc
  sTag = ''
end

Автор: Serx 9.1.2011, 3:12

ещё бы описание того что делают в регулярных выражениях квадратные скобки...
Насколько я понимаю в них указываются варианты символов, которые могут быть в этом месте строки, а как быть если эти варианты состоят не из одного символа?
Аналогично и для "всяких плюсиков" т.е. возможно ли использовать их относительно группы символов и если да, то как её обозначить?

Автор: Setuper 9.1.2011, 13:58

Квадратные скобки обозначают так называемые символьные класса. Это означает, что на данном месте может стоять любой из символом, который указан в символьном классе.
В символьном классе возможно задать диапазоны символов: [А-Яа-я]. Так выглядит диапазон всех русских букв за исключением Ё и ё.
Что касается плюсика, то к символьному классу можно его применять.

Код
local s="baaabbbababbbaaaaab bababbbababbabbabbab"
print(s:match"([ab]+)") -- Результат: baaabbbababbbaaaaab

Автор: Nickolya 9.1.2011, 15:45

Цитата(Serx @ 9.1.2011, 3:12) *
а как быть если эти варианты состоят не из одного символа?

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

Автор: Ksan 9.1.2011, 16:41

Цитата
а как быть если эти варианты состоят не из одного символа?
Serx, пример приведи..

Автор: Serx 9.1.2011, 17:48

Nickolya уже ответил, то что я и думал (как бы мне этого и не хотелось) big_smile.gif

Ksan: я просто сейчас составляю выражения для антимата (не захотел брать готовый "набор выражений", чтобы самому получше разобраться) и вот сейчас чтобы без мата пишу пример который первый пришел в голову:
допустим хочется перехватить "ложку" и "ложечку" не затрагивая слова "ложе"
у меня были мысли чтобы записать выражение вроде

Код
ло[(ж)(жеч)]ка

как я понял такое в LUA одним выражением не осуществить)

Автор: Ksan 9.1.2011, 18:00

1. Может, поможет это:

Код
"лож[е]?[ч]?ка"

Вряд ли напишут "ложчка" или "ложека"
Ну я проверил, работает (а как же иначе?)
Вот результат в чате:
Цитата
[21:06:07] ложка
[21:06:12] ложечка
[21:06:15] <Test> ложе

2. Ну либо "ложе" вводи в исключения. А насчёт "ложчка" - лично у себя я опечатки и орфографические ошибки не ввожу в исключения, пусть учатся грамотно писать. big_smile.gif

Автор: Setuper 9.1.2011, 18:11

Дело в том, что в регулярке lua, в отличие от перловской регулярки, нету знака "или", который обозначается в перле пайпом - "|".

Что касается указанного примера:

Код
"лож[е]?[ч]?ка"
то тут можно обойтись и без символьных классов:
Код
"ложе?ч?ка"

Автор: Ksan 9.1.2011, 18:16

Не подумал.
Но по большому счёту там могли быть и 2 и больше символов, просто это пример такой простой. big_smile.gif

Автор: MIKHAIL 8.12.2011, 1:03

Подскажите, пожалуйста, как организовать захват всего http://mydc.ru/r/?http://pogoda.by/rss2/cityrss.php?q=26850, что удовлетворяет условию:

Код
..:match("<pubDate>(.-)</pubDate>")

При таком шаблоне у меня, естественно, только одно совпадение и, соответственно, один захват. Что надо дописать в шаблон, чтобы происходил захват всех таких совпадений? Логика подсказывает, что нужен плюс "+", только вот куда его вставить?

Автор: Ksan 8.12.2011, 2:51

Попробуй внедрить в свой скрипт эту процедуру (названия переменных можешь изменить как тебе понравится):

Код
t = {}
for k, v in sMsg:gmatch("<pubDate>.-</pubDate>") do
    local s = v:match("<pubDate>(.-)</pubDate>")
    t[k] = s
end

При выполнении все строки, содержащее нужную тебе информацию (в данном случае время/дату) сохранятся в таблицу t
Потом сможешь вытаскивать их когда и как тебе угодно, например, так:
Код
local sTime = t[1]

дальше это вставляешь в своё выводимое сообщение в нужное место.

Автор: Alexey 8.12.2011, 2:54

Используй конструкцию типа:

Код
for v in sData:gmatch"<pubDate>(.*)</pubDate>" do
...
end

Внутри этого цикла переменной v будет поочерёдно присвоено значение из всех захватов по маске.

PS: Страницу с погодой я не смотрел и потому правильность маски не гарантирую.
PPS: Ну Ksan!!! Опередил... :P

Автор: Xomut 23.1.2012, 10:01

Здавствуйте) Подскажите а какой символ в регулярных выражения lua используется для отступов(табуляции)??? в каждой строке есть несколько отступов, нужно найти содержимое между ними, ниче не могу придумать(((

Автор: Артём 23.1.2012, 11:50

Xomut

Код
%s - \t \n \v \f \r <space>

Автор: Setuper 23.1.2012, 16:42

можно заюзать "\t+(%S*)"

Автор: MIKHAIL 4.3.2012, 2:08

Захват с начала строки понятен. Но как организовать захват с конца строки до ближайшего символа "/" влево в случае, если строка по длине непостоянна и количество директорий может меняться. Например, захватить имя файла (может меняться) с расширением (может меняться) – "sFileName.sExt":

Код
http://sHostName/sDir1/sDir2/.../sDirN/sFileName.sExt

Автор: Enyby 4.3.2012, 2:22

"[^/]+$"

Автор: MIKHAIL 4.3.2012, 2:57

Поиск/захват строки всегда идёт только слева направо, т.е. с начала строки и до конца, или есть вариант наоборот – справа налево, т.е. с конца строки и до начала без каких-либо лишних сортировок?

Автор: Enyby 4.3.2012, 3:01

Слева направо. Но тебе никто не мешает сделать реверс строки (string.reverse), а потом гонять регулярки. К найденным кусками потом снова применить реверс и будет поиск справа налево.

Автор: Ksan 4.3.2012, 3:56

MIKHAIL:

Код
local sFilePath = "http://sHostName/sDir1/sDir2/sDir3/sDirN/sFileName.sExt"
local sFileName = sFilePath:gsub(".-/", "")

Автор: MIKHAIL 4.3.2012, 12:23

Ksan, наверное, имел в виду:

Код
local sFileName = sFilePath:gsub(".*/", "")

Автор: Setuper 4.3.2012, 13:16

Реверс делать не эффективно. Эффективнее ограничиться правильной регуляркой.
Замена gsub тоже не эффективный вариант, так как заменять каждый символ затратно.

Вариант 1:

Код
local sFileName = sFilePath:match".+/(.*)$"


Вариант 2:
Код
local sFileName = sFilePath:match"[^/]*$"


В случае если после последнего слеша ничего нету, то в обоих вариантех получим пустую строку.
В случае если слешей вообще нету, то в первом варианте получим nil, во втором варианте получим всю строку.

Какой вариант лучше - решать самому скриптописателю.

Автор: Ksan 4.3.2012, 21:21

Setuper, я имел в виду именно то, что написал.
Перед написанием сюда я проверил у себя.
Но если настаиваешь на "*" вместо "-", то было бы проще сразу написать, почему (именно для данного случая), потому что правильный ответ выдаёт и так и иначе. При любых видоизменениях исходного текста пути.

Автор: GULAM33 5.10.2012, 8:15

У меня деликатный вопрос по поводу регулярных выражений. Функция sData = string.gsub скрипт анти-мат.
Например, надо поймать матерное слово [X][Y][Z]
sData = string.gsub(sData, "[X][Y][Z]",
Но [X][Y][Z] - является продолжением допустимого слова [a][b][c][d]-[X][Y][Z].
А так же [X][Y][Z] является началом другого допустимого слова [X][Y][Z]-[a][b][c][d].
И ещё [X][Y][Z] является внутренней частью (корень, суффикс) слова [a][b][c][d]-[X][Y][Z]-[a][b][c][d]

sData = string.gsub(sData, "%s[бX][лY][яZ]%s", replWord) , где replWord - замена мата на безобидное слово, %s - пробельный символ.
Исключения: <оскор_бля_ть>, <о_бля_пать> и так далее.

-При детектировании необходимо опускать допустимые слова, то что у меня получилось введение пробела %s

Необходимо фиксировать слово когда юзер пишет в чат сразу после своего nick'a [X][Y][Z], без пробела на конце.
Выводится в чат, если вводить просто запрещенное слово без пробела между ником и словом и без пробела после слова:
01:20:03 <NICK> XYZ

Как исправить эту ситуацию, какое регулярное выражение необходимо применить?

Автор: Setuper 5.10.2012, 9:23

Код
"%sXYZ$"


Если скрипт для PtokaX, то предварительно нужно удалить символ | с конца строки, или же писать так:
Код
"%sXYZ|$"

Автор: GULAM33 5.10.2012, 16:20

Спасибо, я подозревал о наличии "невидимого пробела" .

Автор: GULAM33 10.10.2012, 1:15

Выяснил следующее. Одного вида захвата будет не достаточно.
Так как захват "%sСЛОВО|$" будет работать в случае, если после СЛОВА не будет пробела. А так выводит в чат
Поэтому необходимо СЛОВО захватить двумя разными захватами чтобы запрещённые слова не выводились в чат: "%sСЛОВО|$" и "%sСЛОВО%s".

Автор: GULAM33 13.10.2012, 2:06

Данный символ " является инструментом для захвата. Является ли символ магическим. Как экранировать данный символ? Пример экранирования любого магического символа мне не помог. (%.) и (%") .

Автор: Saymon21 13.10.2012, 2:21

Цитата
Как экранировать данный символ?

Код
string = "\"Hello world\""

Тоже самое будет и для одинарных ковычек.

Автор: GULAM33 14.10.2012, 1:12

А захват данного символа как осуществить?

Автор: Setuper 15.10.2012, 9:06

\" - это Lua экранирование, так как кавычка является символом синтаксиса Lua (в отличие от символа %, который является экранирующим символом в регулярном выражении).
Захватить символ можно скобками:

Код
str:find"(\")"

можно написать так:
Код
str:find'(")'


То есть, экранировать кавычки нужно в том случае, когда эти же кавычки обозначают строку.

Автор: GULAM33 22.10.2012, 10:34

Огромное спасибо Setuperу, помогли разобраться .

Автор: MIKHAIL 12.8.2014, 22:17

Что за %с – контрольный символ? Пожалуйста, приведите пример.

Автор: Ksan 13.8.2014, 0:39

MIKHAIL,
Подозреваю, это непечатаемые символы с цифровым кодом до 32 (от 0 до 31).
Как пример - \r, \n, \t Надеюсь, они вам знакомы? big_smile.gif
Вот полный перечень этих символов:
%c: \0 \1 \2 \3 \4 \5 \6 \a \b \t \n \v \f \r \14 \15 \16 \17 \18 \19 \20 \21 \22 \23 \24 \25 \26 \27 \28 \29 \30 \31
Возможно, есть ещё один символ.
Где-то на форуме было описание каждого из них.

Автор: Alexey 13.8.2014, 1:27

Неправильный перевод. Это http://mydc.ru/r/?https://ru.wikipedia.org/wiki/%D0%A3%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D1%8F%D1%8E%D1%89%D0%B8%D0%B5_%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D1%8B.
Для ответов на подобные вопросы даже http://mydc.ru/topic3417.html выложили.