前言#
今天是我每天一篇技术总结计划的第60天,也就是说我坚持每天一篇技术总结已经整整两个月了,我先给自己点个赞。今天也正好是lua函数中string
家族里讲解完结的一章,在这一段时间中我们经常会在章节中听到模式匹配这个词,这也是string库函数的强大之处。
Lua并不使用POSIX规范的正则表达式来进行模式匹配。主要的原因出于程序大小方面的考虑:实现一个典型的符合POSIX标准的regexp大概需要4000行代码,这比整个Lua标准库加在一起都大。权衡之下,Lua中的模式匹配的实现只用了500行代码,当然这意味着不可能实现POSIX所规范的所有功能。但是Lua中的模式匹配功能也是很强大的,并且包含了一些使用标准POSIX模式匹配不容易实现的功能。
前面几章一直跟大家说会总结一下模式匹配这一块的内容,今天是周末我就利用多一点时间,根据lua官方文档中关于模式的介绍为主线,我们一起来总结一下lua中关于模式匹配的内容,其中大部分内容官方文档已经介绍过了,我就是翻译一下,可能有理解不正确的地方,欢迎大家批评指正。
内容#
string patterns##
1.Character Class
一个字符类可以被用于表示一组字符,以下组合被允许来来描述一个字符类:
-
x
:表示字符x
本身(这里的x表示不是魔法字符^$()%.[]*+-?
中的一个)。 -
.
:表示所有字符(一个点/英文中句号)。 -
%a
:表示所有字母。 -
%c
:表示所有控制字符。 -
%d
:表示所有十进制数字。 -
%l
:表示所有小写字母。 -
%p
:表示所有标点符号。 -
%s
:表示所有空白字符。 -
%u
:表示所有大写字母。 -
%w
:表示所有字母和数字。 -
%x
:表示所有十六进制数。 -
%z
:表示0值字符。 -
%x
:表示字符x
(此处的字符x
不是字母或数字)。这是讲魔术字符转义的标准方式,当被用于在模式中表示自身时,任何标点符号(甚至非魔术的)都能加一个前缀%
。 - [set]:表示set中的所有字符的联合构成的分类。通过用
-
分隔截止字符可以指定某个范围的字符。上面描述的所有种类的%x都可用作set的组成部分。set中的所有其他字符表示它们自身。例如[%w_]
(或[_%w]
)表示所有字母数字字符和下划线,[0-7]
表示八进制数字,[0-7%l%-]
表示八进制数字和小写字母以及-
字符。 - [^set]:表示set的补集,其中的set在上面解释了。
- 字符范围和字符类之间的相互作用是未定义的。因此类似[%a-z]或[a-%%]的模式没有意义。
- 对于所有单字母表示的字符类(
%a
、%c
等等),相应的大写字母表示该字符类的补集。例如,%S
表示所有非空白符。 - 字母、空白和其他字符组合的定义依赖于当前locale。特别地,字符类[a-z]可能不等于%l。
2.Pattern Item
一个模式选项可以是:
- 一个单个字符类,它匹配该类中的任意单个字符。
- 一个后跟
*
的单个字符类,它匹配该类中的0或多个字符。这些重复项将总是匹配最长的可能序列。 - 一个后跟
+
的单个字符类,它匹配该类中的1或多个字符。这些重复项将总是匹配最长的可能序列。 - 一个后跟
-
的单个字符类,它也匹配该类中的0或多个字符。与*
不同,这些重复项将总是匹配最短的可能序列。 - 一个后跟
?
的单个字符类,它匹配出现0或1次该类中的字符。 -
%n
中的n
在1和9之间,这种项匹配一个等价于捕获的字符串的第n
个子串(见下面) -
%bxy
中x和y是两个不同的字符;这种项匹配始于x终于y的字符串,并且x
和y
是对称的。这表示,如果一个人从左到右读字符串,对x
计数为+1,对y
计数为-1,结尾的y是第一个遇到计数为0的y。例如,项%b()
匹配带有平衡的圆括号的表达式。
3.Pattern
模式是一系列的模式项。在模式开头的^
将匹配固定在源串的开头。在模式结尾的$
将匹配固定在源串的结尾。在其他位置上,^
和$
没有特殊含义,表示它们自身。
4.Captures
模式可以含有括在圆括号内的子模式,它们描述捕获。当成功进行一个匹配,源串中匹配捕获的子串被存储(捕获)以便将来使用。捕获根据它们的左圆括号进行编号。例如,在模式"(a*(.)%w(%s*))"
中,字符串的匹配"a*(.)%w(%s*)"
的部分作为第一个捕获被存储(因此被编号为1),匹配"."
的字符被捕获并编号为2,匹配"%s*"
的部分被编号为3。
作为一种特殊情况,空捕获()捕获当前字符串位置(一个数字)。例如,如果我们把模式()aa()
用于字符串"flaaap"
,将有两个捕获:3和5。
模式不能含有内嵌的0(即'\0'
)。使用%z
代替。
usage##
- 首先我们新建一个文件将文件命名为patterntest.lua然后编写代码如下:
-- 这是一个原串
local sourcestr = "ehrt999wj=--=-*-/4mdqwl\0ds123tfef"
print("\nsourcestr = "..string.format("%q",sourcestr));
-- '%z'的使用
local match_ret = string.match(sourcestr, "%z")
print("\n%z match_ret is ", string.format("%q",match_ret))
-- '*'的使用
match_ret = string.match(sourcestr, "%a*")
print("\n%a* match_ret is ", string.format("%q",match_ret))
-- '-'的使用(其实我有点迷糊)
match_ret = string.match(sourcestr, "%a%d-")
print("\n%a- match_ret is ", string.format("%q",match_ret))
-- '%bxy'的使用
match_ret = string.match(sourcestr, "%bhs")
print("\n%bhs match_ret is ", string.format("%q",match_ret))
-- '()xxx()'的使用
local match_ret1, match_ret2 = string.match(sourcestr, "()t9()")
print("\n()t9() match_ret is ", string.format("%q",match_ret1),
string.format("%q",match_ret2))
- 运行结果
总结#
- 为了测试其中一些字符类的效果,我们使用了函数
string.match()
这个相对简单的函数,可以说明问题就行了。 - 在代码中我们举了几个不太好理解的例子,比如
%z
、%bxy
、%a*
等,大家可以看一下输出结果感受一下。 - 在例子中特别是最后一个不太好理解,我的理解是第一个括号输出的索引是捕获字符串的第一个位置,后一个括号输出的是捕获字符串结束的后一个位置。