lpeg攻略


前言

最近看到lua的一个库lpeg,记录一下学到的


提示:以下是本篇文章正文内容

一、lpeg是什么?

LPeg文本匹配的工具,比Lua原生的字符串匹配和标准正则表达式更优异。

二、使用步骤

1.引入库

代码如下(示例):

local lpeg = require "lpeg"

2.起别名

代码如下(示例):

--起别名
match = lpeg.match
P = lpeg.P
S = lpeg.S
R = lpeg.R

目的: 少打点代码

3. 常用操作速览

OperatorDescription
lpeg.P(string)Matches string literally
lpeg.P(n)Matches exactly n characters
lpeg.S(string)Matches any character in string (Set)
lpeg.R("xy")Matches any character between x and y (Range)
patt^nMatches at least n repetitions of patt
patt^-nMatches at most n repetitions of patt
patt1 * patt2Matches patt1 followed by patt2
patt1 + patt2Matches patt1 or patt2 (ordered choice)
patt1 - patt2Matches patt1 if patt2 does not match
-pattEquivalent to ("" - patt)
#pattMatches patt but consumes no input
lpeg.B(patt)Matches patt behind the current position, consuming no input

4.P、R、S

-- P匹配字符串 从头开始找,返回第一次找到的位置+1
print(match(P'a',  'abc'))	--2
print(match(P'b',  'abc'))	--nil
print(match(P'ab', 'abc'))	--3
print(match(P'a',  'aaa'))	--2
print(match(P'ab', 'abab'))	--3

-- R匹配范围,09代表0~9,AZ同理,也是从头开始找
print(match(R'az',  'abc'))	--2
print(match(R'aZ',  'abc'))	--nil
print(match(R'AZ',  'abc'))	--nil
print(match(R'Az',  'abc'))	--2
print(match(R'az',  '0abc'))--nil

-- S集合匹配 从头开始 若字符串包含集合内元素,匹配成功(ps:那岂不是匹配成功就一定是2,后面再看)
print(match(S'abc', 'abc')) --2
print(match(S'bc',  'abc'))	--nil
print(match(S'1a',  'abc'))	--2

4.多次匹配+、*

-- ^n代表至少匹配n次 ^-n代表至多匹配n次
print(match(P'a'^1,   'aaaa'))	--5
print(match(P'a'^4,   'aaaa'))	--5
print(match(P'a'^5,   'aaaa'))	--nil
print(match(P'aa'^2,  'aaaa'))	--5
print(match(P'ab'^2,  'ababc'))	--5
print(match(P'ab'^-1, 'ababc'))--3

-- *代表且 例如:P'a'*P'b'^0代表开头是a且后面跟至少0个b
print(match(P'a'*P'b'^0, 'aab')) --2
print(match(P'a'*P'b'^1, 'aab')) --nil
print(match(P'a'*P'b'^1, 'abab'))--3
print(match(P'a'^0*P'b', 'aab')) --4
print(match(P'a'^1*P'b', 'aab')) --4

-- +代表或 例如:P'a' + P'b'^1 代表开头是a 或 开头至少1个b
local a_1b = P'a' + P'b'^1
print(match(a_1b, 'ab')) --2
print(match(a_1b, 'bb')) --3
print(a_1b:match  'bc')  --2(小括号的语法糖 f('x')-> f 'x')

5.练习

  • 匹配换行
m.P'\r\n' + m.S'\r\n'	--开头是\r\n 或 \r 或 \n
  • 数字
digit = R'09' -- 一个数字
digits = digit^1 -- 任意整数

6.基本捕获

OperationWhat it Produces
lpeg.C(patt)the match for patt plus all captures made by patt
lpeg.Carg(n)the value of the nth extra argument to lpeg.match (matches the empty string)
lpeg.Cb(name)the values produced by the previous group capture named name (matches the empty string)
lpeg.Cc(values)the given values (matches the empty string)
lpeg.Cf(patt, func)a folding of the captures from patt
lpeg.Cg(patt [, name])the values produced by patt, optionally tagged with name
lpeg.Cp()the current position (matches the empty string)
lpeg.Cs(patt)the match for patt with the values from nested captures replacing their matches
lpeg.Ct(patt)a table with all captures from patt
patt / stringstring, with some marks replaced by captures of patt
patt / numberthe n-th value captured by patt, or no value when number is zero.
patt / tabletable[c], where c is the (first) capture of patt
patt / functionthe returns of function applied to the captures of patt
lpeg.Cmt(patt, function)the returns of function applied to the captures of patt; the application is done at match time
--别名
C = lpeg.C
Ct = lpeg.Ct
--构造捕获器
match_digits = C(digits)
print(match_digits:match '1233210')	--1233210
print(match_digits:match '+123')	--nil
--兼容开头正负号
int = S'+-'^-1 * digits
match_int = C(int)	--至多一次的正或负+数字
print(match_int:match '+123')		--+123
-- 对捕获后的数据进行处理    关键字\将所有匹配的子串丢给一个函数或者table
match(int/tonumber,'+123') + 1		--124
match(C(P'a'^1) * C(P'b'^1), 'aabbbb') --先当于'^(a+)(b+)'匹配然后捕获两次

7.复杂的例子:匹配浮点数

正则表达式版本

'[-+]?[0-9]+\.?[0-9]+([eE][+-]?[0-9]+)?'

lpeg版本

-- maybe(p)  可能有p
function maybe(p) return p^-1 end

digits = R'09'^1
sign = maybe(S'+-')
dot = '.'
exp = S'eE'
-- 浮点数 = 正负号 + 整数 + 小数 + 科学计数法
float = sign * digits * maybe(dot*digits) * maybe(exp*mpm*digits)

print(match(C(float),'3.1415926'))	--3.1415926
print(match(C(float),'3.14.15'))	--3.14
print(match(C(float),'3.14e-3'))	--3.14e-3

匹配以逗号分隔的多个浮点数

listf = C(float) * (',' * C(float))^0
print(listf:match '2,3,4') -- 2 3 4

-- 把结果存一张表里
--~ 1	3.140
--~ 2	1.590
--~ 3	2.0e-10
local t_match = match(Ct(listf),'3.140,1.590,2.0e-10')
for	k,v in pairs(t_match) do
	print(k,v)
end


--number版本(之前是string)
--~ 1	3.14
--~ 2	1.59
--~ 3	2e-010
floatc = float/tonumber
listf = floatc * (',' * floatc)^0
floatc = float/tonumber
listf = floatc * (',' * floatc)^0
t_match = match(Ct(listf),'3.14,1.59,2.0e-10')
for	k,v in pairs(t_match) do
	print(k,v)
end

-- 兼容空格
sp = P' '^0 --任意数量空格
function space(pat) return sp * pat * sp end --匹配前后空格
floatc = space(float/tonumber) 
listc = floatc * (',' * floatc)^0
print(match(Ct(listc),' 1,2, 3'))

8.更为复杂的例子 读取文本多少行多少字 输出第几行

x = [[a=123456

b=2
c=3456789]]
local m = require 'lpeglabel'

local row
local fl
-- 行尾符
local NL = (m.P'\r\n' + m.S'\r\n') * m.Cp() / function (pos)
    -- 行数++
    row = row + 1
    -- 行尾前面有多少数 如果最后不是行尾,col不为0
    fl = pos
end
-- 一个字一个字匹配   若干(空白符或者一个字) 匹配了所有字
local ROWCOL = (NL + m.P(1))^0

local function rowcol(str, n)
    row = 1
    fl = 1
    ROWCOL:match(str:sub(1, n))
    local col = n - fl + 1
    return row, col
end

--空行
local NL = m.P'\r\n' + m.S'\r\n'

local function line(str, row)
    local count = 0
    local res
    --以出现换行符或者行尾无换行符为一行
    local LINE = m.Cmt((1 - NL)^0, function (a, b, c)
-- print(a,b,c)
-- 第一次打印
-- a:'a=13456\n\nb=2\nc=3456789'
-- b:9
-- c:'a=123456'
        count = count + 1
        if count == row then
            res = c
            return false
        end
		a = 1
        return true
    end)
    --每一行
    local MATCH = (LINE * NL)^0 * LINE
    MATCH:match(str)
    return res
end


print(rowcol(x,#x));   	 --4 9 四行 最后一行9字符
print(line(x,4))       		 --c=3456789 打印第4行


拓展阅读

[1] LPeg - Parsing Expression Grammars For Lua

[2] LPEG and regular expressions - comparison and tutorial

[3] Lpeg Recipes

[4] lua-inspect

[5] LuaFish
LuaFish 开源库提供了各种 Lua 模块,用于通过LuaPeg将Lua 5.1 源代码解析为抽象语法树 (AST) 并将 AST 序列化回 Lua 代码。它还具有对 LISP 风格的宏、静态类型检查和将 Lua 编译为 C 的实验性支持。

  • 29
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值