lua之字符串模式匹配
在string库中功能最强大的函数是:
string.find(字符串查找) string.gsub(全局字符串替换) string.gfind(全局字符串查找) string.gmatch(返回查找到字符串的迭代器) 这些函数都是基于模式匹配的。与其他脚本语言不同的是,Lua并不使用POSIX规范的正则表达式[4](也写作regexp)来进行模式匹配。主要的原因出于程序大小方面的考虑:实现一个典型的符合POSIX标准的regexp大概需要4000行代码,这比整个Lua标准库加在一起都大。权衡之下,Lua中的模式匹配的实现只用了500行代码,当然这意味着不可能实现POSIX所规范的所有更能。然而,Lua中的模式匹配功能是很强大的,并且包含了一些使用标准POSIX模式匹配不容易实现的功能。
string.gmatch(str,pattern)????? 这是一个返回迭代器的函数. 实际的用例如下:
s = "hello world from Lua" for w in string.gmatch(s,"%a+") do print(w) end 这里是一个捕获并将配对字符分别存到不同变量的例子:
t = {} s = "from=world,to=Lua" for k,v in string.gmatch(s,"(%w+)=(%w+)") do t[k]=v end for k,v in pairs(t) do print(k,v) end string.gsub(str,pattern,repl,n)
pattern 的子串都将被参数repl 所指定的字符串所替换,如果指定了参数m ,那么只替换查找过程的前m 个匹配的子串,参数repl 可以是一个字符串、表、或者是函数,并且函数可以将匹配的次数作为函数的第二个参数返回,接下来看看参数repl 的含义:
以下是几个例子:
-- 常规替换
x = string.gsub("hello world","(%w+)","lua")
print("n",x)
-- 都用匹配的第一个串*2来替换
x = string.gsub("hello world","%1 %1")
print("n",x)
-- 用匹配出的完成串*2来替换第一次匹配的结果
x = string.gsub("hello world","%w+","%0 %0",1)
print("n",x)
-- 使用一个完整匹配和一个匹配的第二个串来替换
x = string.gsub("hello world from c to lua","(%w+) (%a+)","%0 %2")
print("n",x) --%0:hello world;%1:hello;%2:world;
-- 调用系统函数来替换
x = string.gsub("os = $OS,pathext = $PATHEXT","%$(%w+)",os.getenv)
print("n",x)
-- 调用自定义函数
x = string.gsub("4 + 5 = $return 4+5$","%$(.-)%$",function (s)
return loadstring(s)()
end)
print("n",x)
-- 调用表来替换
local t = {name="lua",version="5.1"}
x = string.gsub("$name-$version.tar.gz",t)
print("n",x)
string.match(str,init) string.match()只寻找源字串str中的第一个配对. 参数init可选,指定搜寻过程的起点,默认为1. 在成功配对时,函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记,则返回整个配对字符串. 当没有成功的配对时,返回nil.
string.match("abcdaef","a") -> a string.reverse(str) 返回一个字符串的倒序排列
复制代码代码如下:
string.reverse("abcde") ->edcba string.dump(function) 返回指定函数的二进制代码(函数必须是一个Lua函数,并且没有上值) string.find(str,init,plain) string.find的基本应用就是用来在目标串(subject string)内搜索匹配指定的模式的串。函数如果找到匹配的串返回他的位置,否则返回nil.最简单的模式就是一个单词,仅仅匹配单词本身。比如,模式'hello'仅仅匹配目标串中的"hello"。当查找到模式的时候,函数返回两个值:匹配串开始索引和结束索引。 复制代码代码如下: s = "hello world" string.find(s,"hello")??? --> 1??? 5 string.find(s,"world")??? --> 7??? 11 string.find(s,"l")??????? --> 3??? 3 string.find(s,"lll")????? --> nil string.find函数第三个参数是可选的:标示目标串中搜索的起始位置。当我们想查找目标串中所有匹配的子串的时候,这个选项非常有用。我们可以不断的循环搜索,每一次从前一次匹配的结束位置开始。下面看一个例子,下面的代码用一个字符串中所有的新行构造一个表: 复制代码代码如下: local t = {}????? -- 存放回车符的位置 local i = 0 while true do ??? i = string.find(s,"n",i+1)? -- 查找下一行 ??? if i == nil then break end ??? table.insert(t,i) end string.sub(str,sPos,ePos) string.gsub的功能是截取字符串,他从指定起始位置截取一个字符串。string.sub可以利用string.find返回的值截取匹配的子串。 复制代码代码如下: s = "hello world" local i,j = string.find(s,"hello")??? --> 1??? 5 string.sub(s,i,j)??????? --> hello string.gsub(str,sourcestr,desstr) string.gsub的基本作用是用来查找匹配模式的串,并将使用替换串其替换掉: string.gsub函数有三个参数:目标串,模式串,替换串。 复制代码代码如下: s = string.gsub("Lua is cute","cute","great") print(s)????? --> Lua is great s = string.gsub("all lii","l","x") print(s)????? --> axx xii s = string.gsub("Lua is great","perl","tcl") print(s)????? --> Lua is great 第四个参数是可选的,用来限制替换的范围: 复制代码代码如下: s = string.gsub("all lii","x",1) print(s)????????? --> axl lii s = string.gsub("all lii",2) print(s)????????? --> axx lii string.gsub的第二个返回值表示他进行替换操作的次数。例如,下面代码涌来计算一个字符串中空格出现的次数: 复制代码代码如下: _,count = string.gsub(str," "," ") (注意,_ 只是一个哑元变量) 模式 你还可以在模式串中使用字符类。字符类指可以匹配一个特定字符集合内任何字符的模式项。比如,字符类%d匹配任意数字。所以你可以使用模式串'%d%d/%d%d/%d%d%d%d'搜索dd/mm/yyyy格式的日期: 复制代码代码如下: s = "Deadline is 30/05/1999,firm" date = "%d%d/%d%d/%d%d%d%d" print(string.sub(s,string.find(s,date)))??? --> 30/05/1999 下面的表列出了Lua支持的所有字符类: 单个字符(除^$()%.[]*+-?外): 与该字符自身配对 .(点): 与任何字符配对 当上述的字符类用大写书写时,表示与非此字符类的任何字符配对. 例如,%S表示与任何非空白字符配对.例如,'%A'非字母的字符 复制代码代码如下: print(string.gsub("hello,up-down!","%A",".")) ??? --> hello..up.down. 4 (数字4不是字符串结果的一部分,他是gsub返回的第二个结果,代表发生替换的次数。下面其他的关于打印gsub结果的例子中将会忽略这个数值。)在模式匹配中有一些特殊字符,他们有特殊的意义,Lua中的特殊字符如下: 复制代码代码如下: ( ) . % + - * ? [ ^ $ '%' 用作特殊字符的转义字符,因此 '%.' 匹配点;'%%' 匹配字符 '%'。转义字符 '%'不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。当对一个字符有疑问的时候,为安全起见请使用转义字符转义他。 对Lua而言,模式串就是普通的字符串。他们和其他的字符串没有区别,也不会受到特殊对待。只有他们被用作模式串用于函数的时候,'%' 才作为转义字符。所以,如果你需要在一个模式串内放置引号的话,你必须使用在其他的字符串中放置引号的方法来处理,使用 '' 转义引号,'' 是Lua的转义符。你可以使用方括号将字符类或者字符括起来创建自己的字符类(译者:Lua称之为char-set,就是指传统正则表达式概念中的括号表达式)。比如,'[%w_]' 将匹配字母数字和下划线,'[01]' 匹配二进制数字,'[%[%]]' 匹配一对方括号。下面的例子统计文本中元音字母出现的次数: 复制代码代码如下: 在char-set中可以使用范围表示字符的集合,第一个字符和最后一个字符之间用连字符连接表示这两个字符之间范围内的字符集合。大部分的常用字符范围都已经预定义好了,所以一般你不需要自己定义字符的集合。比如,'%d' 表示 '[0-9]';'%x' 表示 '[0-9a-fA-F]'。然而,如果你想查找八进制数,你可能更喜欢使用 '[0-7]' 而不是 '[01234567]'。你可以在字符集(char-set)的开始处使用 '^' 表示其补集:'[^0-7]' 匹配任何不是八进制数字的字符;'[^n]' 匹配任何非换行符户的字符。记住,可以使用大写的字符类表示其补集:'%S' 比 '[^%s]' 要简短些。 Lua的字符类依赖于本地环境,所以 '[a-z]' 可能与 '%l' 表示的字符集不同。在一般情况下,后者包括 '?' 和 '?',而前者没有。应该尽可能的使用后者来表示字母,除非出于某些特殊考虑,因为后者更简单、方便、更高效。 可以使用修饰符来修饰模式增强模式的表达能力,Lua中的模式修饰符有四个: 复制代码代码如下: +????? 匹配前一字符1次或多次 *????? 匹配前一字符0次或多次 -????? 匹配前一字符0次或多次 ?????? 匹配前一字符0次或1次 '+',匹配一个或多个字符,总是进行最长的匹配。比如,模式串 '%a+' 匹配一个或多个字母或者一个单词: 复制代码代码如下: print(string.gsub("one,and two; and three","%a+","word")) ??? --> word,word word; word word '%d+' 匹配一个或多个数字(整数): 复制代码代码如下: i,j = string.find("the number 1298 is even","%d+") print(i,j)??? --> 12? 15 '*' 与 '+' 类似,但是他匹配一个字符0次或多次出现.一个典型的应用是匹配空白。比如,为了匹配一对圆括号()或者括号之间的空白,可以使用 '%(%s*%)'。( '%s*' 用来匹配0个或多个空白。由于圆括号在模式中有特殊的含义,所以我们必须使用 '%' 转义他。)再看一个例子,'[_%a][_%w]*' 匹配Lua程序中的标示符:字母或者下划线开头的字母下划线数字序列。 '-' 与 '*' 一样,都匹配一个字符的0次或多次出现,但是他进行的是最短匹配。某些时候这两个用起来没有区别,但有些时候结果将截然不同。比如,如果你使用模式 '[_%a][_%w]-' 来查找标示符,你将只能找到第一个字母,因为 '[_%w]-' 永远匹配空。另一方面,假定你想查找C程序中的注释,很多人可能使用 '/%*.*%*/'(也就是说 "/*" 后面跟着任意多个字符,然后跟着 "*/" )。然而,由于 '.*' 进行的是最长匹配,这个模式将匹配程序中第一个 "/*" 和最后一个 "*/" 之间所有部分: 复制代码代码如下: test = "int x; /* x */ int y; /* y */" print(string.gsub(test,"/%*.*%*/","<COMMENT>")) ??? --> int x; <COMMENT> 然而模式 '.-' 进行的是最短匹配,她会匹配 "/*" 开始到第一个 "*/" 之前的部分: 复制代码代码如下: ??? --> int x; <COMMENT> int y; <COMMENT>
'?' 匹配一个字符0次或1次。举个例子,假定我们想在一段文本内查找一个整数,整数可能带有正负号。模式 '[+-]?%d+' 符合我们的要求,它可以匹配像 "-12"、"23" 和 "+1009" 等数字。'[+-]' 是一个匹配 '+' 或者 '-' 的字符类;接下来的 '?' 意思是匹配前面的字符类0次或者1次。 与其他系统的模式不同的是,Lua中的修饰符不能用字符类;不能将模式分组然后使用修饰符作用这个分组。比如,没有一个模式可以匹配一个可选的单词(除非这个单词只有一个字母)。下面我将看到,通常你可以使用一些高级技术绕开这个限制。 复制代码代码如下: if string.find(s,"^%d") then ... 检查字符串s是否以数字开头,而 复制代码代码如下: 检查字符串s是否是一个整数。 复制代码代码如下: print(string.gsub("a (enclosed (in) parentheses) line","%b()","")) --> a line 常用的这种模式有:'%b()' ,'%b[]','%b%{%}' 和 '%b<>'。你也可以使用任何字符作为分隔符。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |