时间:2015-07-02 11:29作者:zhao人气:46
使用一个模式
这个例子显示了一个建立和使用模式的程序,它非常简单但很完整:
local lpeg = require "lpeg"
-- matches a word followed by end-of-string
p = lpeg.R"az"^1 * -1
print(p:match("hello")) --> 6
print(lpeg.match(p, "hello")) --> 6
print(p:match("1 hello")) --> nil
模式是简单的一个或多个小写字符并在尾端以(-1)结束的序列。该程序调用match来当作一个方法和函数。在以上成功案例,匹配函数返回成功 匹配的第一个字符的索引,为其字符串长度加1。
Name-value lists
这个例子解析一个名称 - 值配对的列表,并返回那些配对的表:
lpeg.locale(lpeg) -- adds locale entries into 'lpeg' table
local space = lpeg.space^0
local name = lpeg.C(lpeg.alpha^1) * space
local sep = lpeg.S(",;") * space
local pair = lpeg.Cg(name * "=" * space * name) * sep^-1
local list = lpeg.Cf(lpeg.Ct("") * pair^0, rawset)
t = list:match("a=b, c = hi; next = pi") --> { a = "b", c = "hi", next = "pi" }
每一配对都有 formatname =namefollowed 的一个可选的分离器(用逗号或分号)。 配对模式(Thepairpattern)在一个组模式里形成闭包,那么那些名称就可以成为单个捕获的值。 列表模式 (Thelistpattern)然后折叠这些捕获。 它以空列表开始,通过创建列表捕获匹配一个空字符串,然后为每个捕获(一名称对)appliesrawsetover累加器(表)和捕捉值(对名称)。rawsetreturns((未初始化的集合)返回表本身,所以累加器总是表中执行。
以下代码创建了一个模式,该模式使用给定的分隔模式sep作为分隔器来来拆分字符串:
function split (s, sep)
sep = lpeg.P(sep)
local elem = lpeg.C((1 - sep)^0)
local p = elem * (sep * elem)^0
return lpeg.match(p, s)
end
首先,该函数确保sep一个合适的模式。只要没有匹配分隔器,该模式的elem 是重复的零个或多个任意字符。它还捕捉其匹配值。模式p匹配由sep拆分的一组元素.
如果拆分产生的结果值太多,可能会溢出由一个Lua函数返回的最大数目的值。在这种情况下,我们可以将这些值放到一个表中:
function split (s, sep)
sep = lpeg.P(sep)
local elem = lpeg.C((1 - sep)^0)
local p = lpeg.Ct(elem * (sep * elem)^0) -- make a table capture
return lpeg.match(p, s)
end
模式搜索
基本的匹配仅仅工作在锚定模式下。如果我们打算查找匹配字符串中任何地方的模式,那么我们必须写一个匹配任何地方的模式。
因为模式是可以编写的,所以我们可以编写一个函数,它给定一个任意的模式p,返回一个搜索p的新模式,以匹配字符串的任何位置。执行这种搜索有几种方法。一种方法如下:
function anywhere (p)
return lpeg.P{ p + 1 * lpeg.V(1) }
end
这个语法的直接解读:匹配p或者跳过一个字符,然后试图再次匹配。
如果我们想知道这个模式在字符串的所有匹配位置(而不仅仅知道它在字符串的某个位置),那么我们可以给这个模式添加位置捕捉:
local I = lpeg.Cp()
function anywhere (p)
return lpeg.P{ I * p * I + 1 * lpeg.V(1) }
end
print(anywhere("world"):match("hello world!")) -> 7 12
这种搜索的另一个方法如下:
local I = lpeg.Cp()
function anywhere (p)
return (1 - lpeg.P(p))^0 * I * p * I
end
再次说明,这个模式的直接解读:当不匹配p时,它跳过尽可能多的字符,然后对p进行匹配(外加正确的位置捕捉)。
如果我们打算查找仅仅匹配单词边界的模式的话,那么我们可以使用以下转换:
local t = lpeg.locale()
function atwordboundary (p)
return lpeg.P{
[1] = p + t.alpha^0 * (1 - t.alpha)^1 * lpeg.V(1)
}
end
平衡的括号
以下模式只匹配带有平衡括号的字符串::
b = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
阅读第一个(也是唯一的)所给语法规则,所谓平衡字符串,就是一个开括号,后跟零个或多个非括号字符或者平衡字符串(LPFG.V(1)),最后跟着与开括号能够闭合的结束括号。
全局替换
下面的例子和tostring.gsub所做工作类似。它接收一个母串和一个模式以及一个替换值,然后替代所传入的母串中所有与指定模式匹配的子串为指定的替换值::
function gsub (s, patt, repl)
patt = lpeg.P(patt)
patt = lpeg.Cs((patt / repl + 1)^0)
return lpeg.match(patt, s)
end
作为instring.gsub,替换值可以是一个字串、函数,或者一个表.
逗号分隔值(CSV)
下面的例子将字符串转换成逗号分隔的值,并返回所有的字段:
local field = '"' * lpeg.Cs(((lpeg.P(1) - '"') + lpeg.P'""' / '"')^0) * '"' +
lpeg.C((1 - lpeg.S',n"')^0)
local record = field * (',' * field)^0 * (lpeg.P'n' + -1)
function csv (s)
return lpeg.match(record, s)
end
一个字段或是一个引用的字段(一族可能包含任何字符除单引号,或双引号)或是一个未被引用的字段(不包含逗号,换行符或引号)。一个记录就是一个用逗号分隔的字段列表(以换行符或以字符串结尾)。
就像这样,前面的匹配返回的每个字段都是独立返回的。若我们添加一个列表截取定义的记录。返回的将不再是一个独立的包含所有字段的列表。
local record = lpeg.Ct(field * (',' * field)^0) * (lpeg.P'n' + -1)
UTF-8 和 Latin 1
使用LPeg来将一字符串从UTF-8编码转换成Latin 1(ISO 88590-1),这并不困难:
-- convert a two-byte UTF-8 sequence to a Latin 1 character
local function f2 (s)
local c1, c2 = string.byte(s, 1, 2)
return string.char(c1 * 64 + c2 - 12416)
end
local utf8 = lpeg.R("