朱皮特的博客 自由的飞翔

lua-regex

2013-03-17
朱皮特
lua
阅读量:

正则表达式规则

下面字符类的大写形式表示小写所代表的集合的补集。例如’%A’表示非字母的字符。

.     任意字符

%a    字母

%c    控制字符(例如\n)

%d    数字

%l    小写字母

%u    大写字母

%p    标点字符

%s    空白符

%w    字母和数字

%x    十六进制数字

%z    代表0的字符

转义字符

( ) . % + - * ? [ ^ $’%’

%. 		匹配点

%% 		匹配字符 %,%不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。当对一个字符有疑问的时候,为安全起见请使用转义字符转义他。

[] 		创建字符集

[%w_] 	匹配字母数字和下划线

[01] 	匹配二进制数字

[%[%]] 	匹配一对方括号

		在[]中使用连字符-%d    表示 [0-9]

%x 		表示 [0-9a-fA-F];[0-7]'表示 [01234567]

		在[]开始处使用 ^ 表示其补集

[^0-7] 	匹配任何不是八进制数字的字符

[^\n] 	匹配任何非换行符户的字符

[^%s] 	等价于%S

模式

你还可以在模式串中使用字符类。字符类指可以匹配一个特定字符集合内任何字符的模式项。比如,字符类%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

%x(此处x是非字母非数字字符): 与字符x配对. 主要用来处理表达式中有功能的字符(^$()%.[]*+-?)的配对问题, 例如%%与%配对 [数个字符类]: 与任何[]中包含的字符类配对. 例如[%w_]与任何字母/数字, 或下划线符号()配对 [^数个字符类]: 与任何不包含在[]中的字符类配对. 例如[^%s]与任何非空白字符配对 当上述的字符类用大写书写时, 表示与非此字符类的任何字符配对. 例如, %S表示与任何非空白字符配对.例如,’%A’非字母的字符 print(string.gsub(“hello, up-down!”, “%A”, “.”)) –> hello..up.down. 4 (数字4不是字符串结果的一部分,他是gsub返回的第二个结果,代表发生替换的次数。下面其他的关于打印gsub结果的例子中将会忽略这个数值。)在模式匹配中有一些特殊字符,他们有特殊的意义,Lua中的特殊字符如下: ( ) . % + - * ? [ ^ $ ‘%’ 用作特殊字符的转义字符,因此 ‘%.’ 匹配点;’%%’ 匹配字符 ‘%’。转义字符 ‘%’不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。当对一个字符有疑问的时候,为安全起见请使用转义字符转义他。 对Lua而言,模式串就是普通的字符串。他们和其他的字符串没有区别,也不会受到特殊对待。只有他们被用作模式串用于函数的时候,’%’ 才作为转义字符。所以,如果你需要在一个模式串内放置引号的话,你必须使用在其他的字符串中放置引号的方法来处理,使用 ‘' 转义引号,’' 是Lua的转义符。你可以使用方括号将字符类或者字符括起来创建自己的字符类(译者:Lua称之为char-set,就是指传统正则表达式概念中的括号表达式)。比如,’[%w]’ 将匹配字母数字和下划线,’[01]’ 匹配二进制数字,’[%[%]]’ 匹配一对方括号。下面的例子统计文本中元音字母出现的次数:

_, nvow = string.gsub(text, "[AEIOUaeiou]", "")

在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>
    

    然而模式 ‘.-‘ 进行的是最短匹配,她会匹配 “/” 开始到第一个 “/” 之前的部分:

      test = "int x; /* x */ int y; /* y */"
      print(string.gsub(test, "/%*.-%*/", "<COMMENT>"))
          --> int x; <COMMENT> int y; <COMMENT>
    
  • ’?’ 匹配一个字符0次或1次。举个例子,假定我们想在一段文本内查找一个整数,整数可能带有正负号。模式 ‘[+-]?%d+’ 符合我们的要求,它可以匹配像 “-12”、”23” 和 “+1009” 等数字。’[+-]’ 是一个匹配 ‘+’ 或者 ‘-‘ 的字符类;接下来的 ‘?’ 意思是匹配前面的字符类0次或者1次。 与其他系统的模式不同的是,Lua中的修饰符不能用字符类;不能将模式分组然后使用修饰符作用这个分组。比如,没有一个模式可以匹配一个可选的单词(除非这个单词只有一个字母)。下面我将看到,通常你可以使用一些高级技术绕开这个限制。 以 ‘^’ 开头的模式只匹配目标串的开始部分,相似的,以 ‘$’ 结尾的模式只匹配目标串的结尾部分。这不仅可以用来限制你要查找的模式,还可以定位(anchor)模式。比如:
	if string.find(s, "^%d") then ...

检查字符串s是否以数字开头,而

	if string.find(s, "^[+-]?%d+$") then ...

检查字符串s是否是一个整数。 ‘%b’ 用来匹配对称的字符。常写为 ‘%bxy’ ,x和y是任意两个不同的字符;x作为匹配的开始,y作为匹配的结束。比如,’%b()’ 匹配以 ‘(‘ 开始,以 ‘)’ 结束的字符串:

	print(string.gsub("a (enclosed (in) parentheses) line", "%b()", ""))
	--> a line

常用的这种模式有:’%b()’ ,’%b[]’,’%b%{%}’ 和 ‘%b<>’。你也可以使用任何字符作为分隔符。

模式匹配相关函数

  • string.find 字符串查找
  • string.gsub 全局字符串替换
  • string.gfind 全局字符串查找
  • string.gmatch 返回查找到字符串的迭代器

捕获

用 () 将要捕获的部分包围起来:

pair = "name = Anna"
firstpos, lastpos, key, value = string.find(pair, "(%a+)%s*=%s*(%a+)")
print(key, value) <== name Anna

拷贝捕获(%1-%9)

s = "abc \"it\'s a cat\""
_,_,_,q = string.find(s, "([\"'])(.-)%1"))print(q) <== it's a cat

如果%d代表第几个捕获的拷贝。string.gsub(s, pattern, reps) 第1个参数:源字符串 第2个参数:待替换之模式串 第3个参数:替换为reps 将s中所有符合pattern的字串替换为reps,返回结果串+匹配数 print(string.gsub(“hello, world”, “o”, “a”)) <== hella, warld

2 gsub也可以用拷贝捕获技巧 print(string.gsub(“hello, world”, “(o)”, “%1-%1”)) <== hello-o, wo-orld

2 print(string.gsub(“hello Lua”, “(.)(.)”, “%2%1”)) <== ehll ouLa

4 function trim (s) return (string.gsub(s, “^%s(.-)%s$”, “%1”)) end <== 注意匹配数用括号丢弃

常见示例

  • 匹配图片
      'src.-([^\"^\'^=^>^%s]+)'
    
  • 匹配email地址
      t = {}
    	
      for name,domain instring.gmatch(s,"([-%a%d%._]+)@([-%a%d.]+)") do-- Really no need to split and rejoin the email address-- it was a part of the demonstration.
          fulladdy = name .. "@" .. domain
          msgbox(fulladdy,'');
      end
    
  • 去除字符串中的0字符 因为Lua字符串是允许含有0字符的,但是在C/C++里是不允许出现的,否则字符串将会被截断出现非预期的结果。 这里使用正则表达式去除Lua字符串中的0字符:
      --可能有终结符
      s = '';
      for name in string.gmatch(wndtext,"[^%z]") do
         s = s..name;
      end
    

    %z表示0字符,^%z表示非零的字符,[]用来创建字符集,整个正则表达式的意思是匹配整个字符串中任何一个非零的字符,循环中再把这些非零字符拼接起来就是去除0字符后的字符串了。


上一篇 lua-closure

Comments

Content