Понимание сопоставления с образцом в Lua

Lua

Недавно в процессе изучения Lua я обнаружил, что в Lua нет встроенной функции регулярных выражений, но я изобрел своего рода правило регулярного выражения — сопоставление шаблонов для поддержки подобных функций. Основной причиной этого является размер программы: POSIX-совместимая реализация регулярных выражений занимает более 4000 строк кода, что больше, чем вся стандартная библиотека Lua.Напротив, реализация сопоставления с образцом в Lua ограничена только 500 строками. . Сопоставление с образцом не так эффективно, как регулярные выражения, но его достаточно для большинства сценариев. Это также можно рассматривать как идею «пространства для времени» — время обучения программистов. В этой статье будут использованы некоторые примеры, которые помогут понять сопоставление с образцом в Lua.

Быстрый просмотр

Следующий пример программы начинается со строкиsИзвлеките часть даты из и распечатайте вывод:

s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.match(s, date))   --> 30/05/1999

где строкаdataЭто «шаблон», используемый для сопоставления. Легко найти,%dпредставляет собой число, говорящееstring.match: вставить строкуsв формеdd/mm/yyyyОтформатированные даты совпадают.

Композиция узора

character classes

аналогичный%dиспользование, вызываемое в Luacharacter classes, перечисленные ниже:

character class соответствовать
%a письмо
%c Управляющие символы (например, возврат каретки:\n)
%d 0-9 номера
%l Строчные буквы
%p Пунктуация
%s пробел
%u Заглавная буква
%w Буквы и цифры
%x шестигранник
%z Нулевой символ "\0"

использоватьcharacter classЗаглавная буква может указывать на дополнение этого типа. Например%AПредставляет все неалфавитные символы:

s, _ = string.gsub("hello, up-down!", "%A", ".")
print(s) --> hello..up.down. 

Процедура функции string.gsub: Сопоставьте "привет, вверх-вниз!" все несимволы в символе (% A) и замените их символами. "."

magic characters

Пока что мы нашлиcharacter classМожет соответствовать только одному персонажу, возможности очень ограничены. Тем не менее, Lua также вводит синтаксис, похожий на регулярные выражения, которые могут выполнять сложное сопоставление с помощью некоторых специальных символов, вызываемых в Lua.magic characters, перечисленные ниже:

magic character описывать Примечание
() Отметьте подшаблон для последующего использования, аналогично обычному использованию
. соответствовать всем символам Подобно регулярному выражению, за исключением того, что регулярное выражение не включает возврат каретки "\n"
% 1. Может использоваться как escape-символ;
2. Заявлениеcharacter classess;
3. В сочетании с () для сопоставления с подшаблоном: %N, N — это число, означающее соответствие N-й подстроки, например %1,%2..., регулярному выражению.1,1,2...похоже.
+ Совпадение с предыдущим шаблоном 1 или более раз
- Соответствует предыдущему режиму 0 раз или несколько раз, возвращает кратчайший результат сопоставления, режим соответствует "режиму без жадности"
* соответствует предыдущему шаблону 0 или более раз
? соответствует предыдущему шаблону 0 или 1 раз
^ Если он находится в начале шаблона, это означает начальную позицию совпадающей входной строки.Если он помещен в [], это означает дополнение, которое в основном совпадает с использованием регулярных выражений.
$ Указывает конечную позицию совпадающей входной строки, что в основном совпадает с использованием регулярных выражений.
[ ] Представляет набор символов (char-set), аналогичный обычному использованию Например, [%w_] означает совпадение букв, цифр и символов подчеркивания, [a-f] означает совпадение букв от a до f.

Пример

В Lua функции, использующие сопоставление с образцом, в основном включают:

сигнатура функции Основное использование
string.find(string, pattern [, init, plain]) Найдите первый экземпляр шаблона в строке и верните его начальную и конечную позиции.
string.match(string, pattern [, init]) Возвращает первый экземпляр в строке, соответствующий шаблону
string.gmatch(string, pattern) Возвращает функцию итератора, которая возвращает следующий сопоставленный экземпляр каждый раз, когда он называется
string.gsub(string, pattern,repl [, n]) Замените все строки, соответствующие шаблону в строке, на repl и верните замененную строку.

Параметры в [] являются необязательными. Подробное описание функций см. в официальной документации.

woohoo.rua.org/manual/5.1/…

Далее мы рассмотрим несколько примеров программ, чтобы продемонстрировать, как использовать сопоставление с образцом.

Базовый пример

совпадение несколько раз

-- + 匹配1次或多次
print(string.match("hello world 123","%w+ %w+")) -->  hello world
print(string.match("helle world 123","[%w %d]+")) --> hello world 123
-- ? 匹配0或1次 (匹配一个有符号数)
print(string.match("the number is: +123", "[+-]?%d+")) --> +123
print(string.match("the number is: 123", "[+-]?%d+")) --> 123
print(string.match("the number is: -123", "[+-]?%d+")) --> -123
-- * 匹配0次或多次
print(string.match("abc123abc", "%a+%d*%a+")) --> abc123abc
print(string.match("abcabc", "%a+%d*%a+")) --> abcabc

использовать[]комбинация

-- 匹配所有字母数字和  "."
print(string.match("a1.a2+a3","[%w.]+")) --> a1.a2
-- 匹配所有数字,等同于%d+,这里只是作为示例,实际编码使用%d+即可
print(string.match("abc123","[0-9]+")) --> 123
-- 匹配所有字母和数字,等同于%w+,这里只是作为示例,实际编码中使用%w+即可
print(string.match("abc123","[a-fA-F0-9]+")) --> abc123
-- 匹配0和1
print(string.match("1010345","[01]+")) --> 1010
-- ^取反, 匹配除了字母以外的所有字符
print(string.match("abc123","[^%a]+")) --> 123

использовать()а также%Nдля захвата подшаблонов

В следующем примере используется()для захвата подшаблонов и возврата результата сопоставления для соответствующего подшаблона:

date = "17/7/1990"
_, _, d, m, y = string.find(date, "(%d+)/(%d+)/(%d+)")
print(d, m, y)  --> 17  7  1990

Этот пример легко понять: мы используем в шаблоне()Определены три подшаблона,string.findВерните результат сопоставления подшаблона в качестве возвращаемого значения соответственно.
использовать()соответствовать%N, вы также можете использовать значение захваченного подшаблона непосредственно в шаблоне, например, извлекать двойные кавычки в строке"или одинарные кавычки'Завернутую часть можно сделать так:

s = [[then he said: "it's all right"!]]
a, b, c, quotedPart = string.find(s, "([\"'])(.-)%1")
print(quotedPart)   --> it's all right
print(c)            --> "

модель([\"'])(.-)%1Содержит два подзаготовителя,%1Это означает, что первый совпавший подшаблон (то есть возвращаемое значениеc), то есть([\"'])Соответствующее значение в этом случае заключено в двойные кавычки.". В этом примере вы не можете просто использовать что-то вроде[\"'].-[\"']Такой шаблон, потому что, очевидно, этот шаблон может соответствовать толькоit.

нежадный матч

+,*,?Правила сопоставления аналогичны жадному сопоставлению в регулярных выражениях, всегда сопоставляя как можно больше. Использование регулярных выражений?для представления нежадного квалификатора. В Луа,?не имеет такой функции, но использует-чтобы указать нежадное сопоставление, например:

print(string.match("<span>hello</span><span>world</span>","<span>.+</span>"))
--> <span>hello</span><span>world</span>
print(string.match("<span>hello</span><span>world</span>","<span>.-</span>")) 
--> <span>hello</span>

Мы хотим сопоставить элементы HTMLspanсодержание между, в первом шаблоне, так как.+Это жадное совпадение, поэтому оно совпадает до конца</span>раньше, то естьhello</span><span>world. Во втором режиме,.-Будет соответствовать как можно меньшему количеству символов, то есть нежадный режим сопоставления (ленивый режим), сопоставление с первым</span>прекратить соответствие.
пройти черезstring.gusbДля просмотра результатов сопоставления:

-- 期望将<span>xxx</span>中的xxx替换成lua
-- 贪婪匹配,替换了整个字符串
print(string.gsub("<span>hello</span><span>world</span>","<span>.+</span>","<span>lua</span>"))
--> <span>lua</span> 

-- 非贪婪匹配,结果符合预期
print(string.gsub("<span>hello</span><span>world</span>","<span>.-</span>","<span>lua</span>"))
--> <span>lua</span><span>lua</span>

обращать внимание,-Не имеет смысла использовать в начале шаблона, потому что-Указывает, что он соответствует 0 или более раз.Если он расположен в начале, это означает, что он всегда будет совпадать 0 раз.

string.gmatch

string.matchТолько первая строка в строке, которая соответствует шаблону шаблона, может быть сопоставлена.Если мы хотим получить все подстроки в строке, которые соответствуют шаблону шаблона, мы можем использоватьstring.gmatch, взяв в качестве примера приведенное выше жадное сопоставление, выведите всеspanЭлементы в тегах:

for i  in string.gmatch("<span>hello</span><span>world</span>", "<span>(.-)</span>") do
    print(i)
end

Выходной результат:

hello
world

Его можно найти на рисунке, мы делаем маленькие руки и ноги, от<span>.-</span>изменился на<span>(.-)</span>. так чтоstring.gmatchВозвращенный итератор будет напрямую перебирать захваченные подшаблоны, т.е.(.-). Если используется исходный шаблон, результат будет следующим:

<span>hello</span>
<span>world</span>

ЭтоgmatchОсобенности: если появляется в выкройке(), итератор будет выполнять только итерацию()Совпадающая подстрока или все совпавшие строки, если нет. С помощью этой функции также можно читать похожиеkey=valueФункции пар ключ-значение:

s = "from=world, to=Lua"
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
    print("key="..k)
    print("value="..v)
end

Выходной результат:

key=from
value=world
key=to
value=Lua

string.gsub

string.gsubСопоставление-замена для строк с использованием параметровreplЗамените все (или первые n, если задано третьим параметром) в строке, которая соответствуетpatternстрока, гдеreplМожет быть строкой, методом или таблицей:

x = string.gsub("hello world", "(%w+)", "%1 %1")
--> x="hello hello world world"
--%1 表示(%w+)所匹配的字符串,也就是"hello world"。

x = string.gsub("hello world", "%w+", "%0 %0", 1)
--> x="hello hello world"
--第三个参数表示替换前1一个,也就是第一个,匹配到的字符串,也就是"hello"。
--对于第一次匹配,$0 $0 = "hello hello"。
--因此得到结果为"hello hello world"。

x = string.gsub("hello world", "%w+", "%0 %0", 2)
--> x="hello hello world world"
--第三个参数表示替换前2个匹配到的字符串,也就是"hello"和"world"。
--对于第一次匹配,$0 $0 = "hello hello".
--对于第二次匹配,$0 $0 = "world world"
--因此得到结果为"hello hello world world"

x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
--> x="world hello Lua from"

x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
--> x="home = /home/roberto, user = roberto"
--%$中的%是转义,表示匹配$
--当第二个参数是函数的时候,这个函数会在每次匹配到模式的时候调用,入参为()所匹配的子模式
--函数返回值则则作为替换的字符串。
--在这个示例中,会调用 os.getenv("HOME") 和 os.getenv("USER"),并根据返回值替换原字符串。

x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
     return loadstring(s)()
   end)
--> x="4+5 = 9"

local t = {name="lua", version="5.1"}
x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
--> x="lua-5.1.tar.gz"

Демонтаж процесса выполнения

string.gsubГеймплей очень насыщенный, но изменения остались прежними. После понимания процесса его выполнения легко понять приведенный выше пример.
string.gsubПроцесс выполнения представляет собой цикл сопоставления с образцом + замена строки до тех пор, пока сопоставление с образцом не прекратится. Псевдокод процесса представляется следующим образом:

repeat 
    执行模式匹配
    根据repl规则,对本次循环模式匹配到的字符串进行替换
until (模式匹配结束)

Если вы не передадите третий параметр,string.gsubПо умолчанию используется точное совпадение строки, если нам не нужно точно соответствовать строке (например, что-то вродеstring.matchа такжеstring.find, соответствует только первым результатам и конце), мы можем использовать третий параметр, чтобы установить, сколько раз соответствует - то есть для установки при заканчивающихся концах шаблона. В текущей цикле замены матча REPL будет воспринимать только результат этого списка шаблонов: например, подстрока, захваченная в% N, является подстрокой, совпадающей в текущем времени, еслиreplявляется функцией, то входным параметром функции также является строка, совпадающая с текущим временем, а возвращаемое значение только заменяет текущую совпадающую строку.

Ссылаться на

woohoo.rua.org/manual/5.1/…
woohoo.rua.org/bulk/20.1.contract…
Woohoo.Защитный чехол.org.UK/wiki/wiki/…
woo woo Краткое описание.com/afraid/post 141027 ой 1…