|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
如果您觉得本篇CentOSLinux教程讲得好,请记得点击右边漂浮的分享程序,把好文章分享给你的好朋友们!每次用到字符串的形式婚配的时分就总要往翻看Lua官网的申明,网上也没有一个对照具体的申明,也有很多多少伴侣都向我扣问这块的内容,实在这块的难点有三:
- 一个是对Lua的正则表达式不熟习;
- 另外一个是对Lua中string库供应的几个函数的用法不熟习;
- 另有一点是Lua的string库提出了一个新的观点,叫做捕捉,实在也不算甚么新观点,只是和函数挪用杂糅在一同举动就欠好了解而已。
这里我总结一下。
先从Lua内置string库供应的几个人人不熟习的函数入手下手(基于Lua5.1,Lua5.2基础没有变更)。
Lua内置字符串库用到形式的中央有4个函数,它们分离是:
- string.find()
- string.match()
- string.gmatch()
- string.gsub()
1、string.find(s,pattern,start,plain)
这个函数的功效是查找字符串s中的指定的形式pattern。
假如找到了一个形式的婚配,就前往找到的形式在s中的出发点和尽头;不然前往nil。这里必要注重的是,它只会前往找到的第一个婚配的地位,以是找到了的前往值实际上是两个数字,婚配的出发点、婚配的尽头。
第三个参数是个数字,它是可选的,start指定在s中查找入手下手的地位,默许是1,start能够是正数,-1代表从最初一个字符入手下手,-2代表倒数第二个字符入手下手。固然,最初都是到最初一个字符停止,以是假如你指定地位从最初一个字符入手下手,那末就只会查找这一个字符。
第四个参数是个bool值,它指明第二个参数pattern中是不是利用特别字符,假如第四个参数指明为true,那末就意味着第二个参数pattern中的那些特别字符(这些字符有^$*+?.([%-,界说在Lua源码lstrlib.c中)都被看成一般字符举行处置,也就是一个复杂的字符串婚配,而不外所谓的形式婚配,也就是不动用正则表达式的婚配。相反,false就意味着pattern接纳特别字符处置。如许说也不太了然,举个例子就分明了,不外要触及到一个Lua形式中特别的字符,假如这里仍是不分明,看了前面我关于Lua正则表达式的先容应当就可以分明。
好比:
1
2
3
locals="am+df"
print(string.find(s,"m+",1,false))--22
print(string.find(s,"m+",1,true))--23
个中字符+在Lua正则表达式中的意义是婚配在它之前的谁人字符一次大概屡次,也就是说m+在正则表达式里会往婚配m,mm,mmm……。以是当string.find第四个参数为false的时分,就只能在字符串s中找到m这个字母是婚配的,那末前往的了局就是22。
而当第四个参数为true的时分,+被看成一般字符,那末查找的婚配就是m+这个字符串,那末找到的地位就是23。
假如你不传第四个参数,就跟false是一个意义。
下面把find函数做了一个复杂的先容,可是这个函数的举动并不是老是如许,为何呢?这就是我文章开首提到的Lua的捕捉也会被杂糅到这些string的库函数里。
没有举措,只得先先容一下所谓的捕捉是个甚么观点。
下面find函数的第二个参数我们都分明是一个形式,能够了解为一样平常的正则婚配中的正则表达式,而Lua为这个形式增添了一个新的功效,也就是所谓的捕捉,在一个形式串中,我们能够用小括号()来标明一些我们想要保留的婚配,而这个小括号中的内容仍然是形式串,也就是说我们只不外是把形式中一些我们想要的特别字符保存上去供前面利用。好比下面谁人例子中的形式串是m+,假如我想要把跟m+婚配的字符串捕捉出来,也就是保留上去,我能够用一个小括号把它括起来,而find函数除下面说到的举动外,也就是除前往查找到pattern的起止地位外,还会前往一切请求捕捉的字符串,像如许:
locals="am+df"
print(string.find(s,"(m+)",1,false))--22m
假如你想要捕捉更多的内容,只必要用小括号把它括起来就行了,好比如许:
locals="am+df"
print(string.find(s,"((m+))",1,false))--22mm
print(string.find(s,"(((m+)))",1,false))--22mmm
关于捕捉另有一点必要申明的,就是捕捉只会在形式可以婚配乐成的时分才会随着string的函数举行前往,好比上面这个,我想捕捉字母a,但现实上这个形式基本没法婚配到,以是一定是没法前往的:
locals="am+df"
print(string.find(s,"(m+)(a)",1,false))--nil
别的捕捉前往的按次,是按照左小括号的地位来定的,好比下面谁人捕捉了3个m的例子,第一个m实际上是最外层的小括号捕捉到的。为何要提到捕捉的按次呢?由于我们可使用%n来获得第n个捕捉的字符串,至于获得对应的捕捉有甚么用途呢?这个在前面会先容到。
一个空的捕捉,也就是小括号内里甚么内容也没有,它会前往以后字符串的对照操纵举行到的地位,好比
locals=”am+df“
print(string.find(s,"()(m+)()",1,false))--222m3
有一点也必需要提一下,就是在Lua5.1的源码傍边,捕捉字符串的数目是无限制的,默许是32个,也就是说你增加的小括号不克不及无穷加,最多加32个。假如捕捉凌驾限定,固然会报错了,好比:
locals=”am+df“
print(string.find(s,"()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()",1,false))--捕捉33个
固然你能够经由过程修正Lua的源码来调剂你想要保留的捕捉数目,这个数目界说在luaconf.h文件中:
一样平常来讲,关于利用,剖析基础到此了,可是关于Lua,由于源码复杂,并且幽美,又是拿C言语写的,心痒难耐,必需要懂得一下源码才解恨。
Lua内置库的加载体例就不说了,在各个年夜神的文章里都能够看到,我们间接来看string.find()这个函数,函数在lstrlib.c文件里:
staticintstr_find(lua_State*L){
returnstr_find_aux(L,1);
}
staticintstr_find_aux(lua_State*L,intfind){
size_tl1,l2;
constchar*s=luaL_checklstring(L,1,&l1);
constchar*p=luaL_checklstring(L,2,&l2);
ptrdiff_tinit=posrelat(luaL_optinteger(L,3,1),l1)-1;
if(init<0)init=0;
elseif((size_t)(init)>l1)init=(ptrdiff_t)l1;
if(find&&(lua_toboolean(L,4)||/*explicitrequest?*/
strpbrk(p,SPECIALS)==NULL)){/*ornospecialcharacters?*/
/*doaplainsearch*/
constchar*s2=lmemfind(s+init,l1-init,p,l2);
if(s2){
lua_pushinteger(L,s2-s+1);
lua_pushinteger(L,s2-s+l2);
return2;
}
}
else{
MatchStatems;
intanchor=(*p==^)?(p++,1):0;
constchar*s1=s+init;
ms.L=L;
ms.src_init=s;
ms.src_end=s+l1;
do{
constchar*res;
ms.level=0;
if((res=match(&ms,s1,p))!=NULL){
if(find){
lua_pushinteger(L,s1-s+1);/*start*/
lua_pushinteger(L,res-s);/*end*/
returnpush_captures(&ms,NULL,0)+2;
}
else
returnpush_captures(&ms,s1,res);
}
}while(s1++<ms.src_end&&!anchor);
}
lua_pushnil(L);/*notfound*/
return1;
}
这个函数开端看起来仍是对照长的,可是细心剖析一下就发明实际上是很复杂的。后面那6行,就是吸收前3个参数而已,只不外处置了一下谁人查找肇端点参数,避免了超越字符串长度。最关头的中央就是紧接着的ifelse逻辑,find是传出去的参数,关于string.find来讲就是1,以是不必管它,以为它一向是真就OK了,既然提到这里了,那末是否是另有其余中央也会挪用这个函数原型的,bingo!我们搜刮一下就会发明,实在string.match()函数实在也是挪用这个函数原型的,而它的find参数就是传送的0。哈哈,岂非string.match函数实在跟string.find函数是一样的?
staticintstr_match(lua_State*L){
returnstr_find_aux(L,0);
}
staticintstr_find(lua_State*L){
returnstr_find_aux(L,1);
}
这个留到先容string.match函数的时分再说。拉返来,持续谈这个ifelse逻辑,if的判别前提实在就是看你挪用string.find的第四个参数,假如第四个参数传送了true,也就是我下面说的,不利用特别字符形式,大概是形式中压根就没有特别字符,谁人SPECIALS宏一样界说在这个文件中:
假如没有这些字符大概是不合错误这些字符特别处置,那末就是一个复杂的字符串婚配,挪用lmemfind()函数,假如找到了,就前往了婚配到的起止地位。
既然云云,那末else里就好了解了,它就是利用特别字符举行婚配的处置,这里的关头函数是match(),它处置字符串和形式举行婚配,并举行了捕捉,这个留到先容形式的时分再接着说。最初假如婚配到了,那末仍旧前往婚配起止点,注重,这里多了一个操纵,就是把捕捉到的字符串也压进了栈。以是我们挪用并捕捉的时分才会有前面那些捕捉的字符串。
这么看来仍是挺好了解的嘛。在猎奇心的趋向下,我十分感乐趣,Lua的谁人lmemfind()函数是怎样举行字符串婚配的,岂非是传说中的KMP又大概是BM算法?假如不熟习这两种算法的童鞋,能够看看阮一峰的这两篇文章:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html,http://www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html
怀着一点小冲动,我点开了lmemfind():
staticconstchar*lmemfind(constchar*s1,size_tl1,
constchar*s2,size_tl2){
if(l2==0)returns1;/*emptystringsareeverywhere*/
elseif(l2>l1)returnNULL;/*avoidsanegative`l1*/
else{
constchar*init;/*tosearchfora`*s2inside`s1*/
l2--;/*1stcharwillbecheckedby`memchr*/
l1=l1-l2;/*`s2cannotbefoundafterthat*/
while(l1>0&&(init=(constchar*)memchr(s1,*s2,l1))!=NULL){
init++;/*1stcharisalreadychecked*/
if(memcmp(init,s2+1,l2)==0)
returninit-1;
else{/*correct`l1and`s1totryagain*/
l1-=init-s1;
s1=init;
}
}
returnNULL;/*notfound*/
}
}
总的来讲,这个对照的***仍是中规中矩的,重新入手下手查找婚配串的第一个字符,只不外用的是memchr函数,找到了以后用memcmp函数来对照两个字符串是不是是不异的,假如不不异就跳过反省了的字符持续。比拟那些庞大的字符串婚配算法,这个既复杂又心爱,赞一个:),memcmp函数的实行天然比str系列的对照要快一些,由于不必一向反省‘ ’字符,关于memcmp函数的做法,这里有一篇文章,固然是说他的优化,可是看他的代码也能大抵懂得memcmp的做法:http://blog.chinaunix.net/uid-25627207-id-3556923.html
2、string.match(s,pattern,start)
这个函数的功效是在字符串s中查找指定的形式pattern,并前往形式pattern中指定的第一个捕捉。
第三个参数指明查找的肇端地位,默许为1。
比拟string.find函数来讲,string.match要复杂的多了,它不必要你再选择是不是接纳特别字符,它必需要接纳。pattern中的内容跟string.find一样,都是一个Lua的形式,跟string.find分歧的中央在于,它前往的不在是婚配到的起止地位,而是前往pattern中指定的第一个捕捉,假如pattern中没有指明捕捉,那末它会前往全部pattern的婚配了局,固然,没有婚配到仍然前往nil。
在先容string.find函数的时分提到过,Lua源码中string.match挪用的函数实在跟string.find挪用的函数是不异的,都是str_find_aux(lua_State*L,intfind)函数,独一分歧的中央在于string.match挪用时find参数传送的是0,如许就不会进进str_find_aux()里复杂婚配的分支,间接举行形式婚配。
3、string.gmatch(s,pattern)
下面先容的两个函数,不管是string.find仍是string.match函数,都是发明和形式相婚配的串后就停上去了,前往对应的内容,而常常我们会有在一个字符串中找到一切跟形式相婚配的串的需求,string.gmatch()函数就可以够满意这个需求。
string.gmatch()函数能够被看成迭代器举行挪用,然后取得一切跟形式相婚配的串,好比Lua官网给出的例子:
s="helloworldfromLua"
forwinstring.gmatch(s,"%a+")do
print(w)
end
--[[
hello
world
from
Lua
]]
至于%a+的意义嘛,在string.find()的先容里提到过字符+的用法,至于%a嘛,它是婚配一切的字母。这里必要注重的是,字符串s里由4个单词,用3个空格举行了分开,以是挪用一次string.gmatch(s,"%a+"),只会婚配s中的第一个单词,由于碰到空格婚配就失利了。
string.gmatch()函数的用法基础也就是在轮回里看成迭代器用了,我还真没发明有其余用法。
一个独一必要注重的中央,就是特别字符^在string.gmatch函数中的用法跟别处是分歧的,在其他的函数中,^的用法是放在一个形式的后面,那末这个形式必需从字符串的最开首入手下手婚配,假如婚配失利则不会持续往和前面的字符婚配了,甚么意义呢,用后面的例子:
1
2
3
locals="am+df"
print(string.find(s,"(m+)",1,false))--22m
print(string.find(s,"^(m+)",1,false))--nil
第二个婚配,由于在形式后面增添了^,以是会从字符串s的最入手下手就举行婚配,也就是从字母a入手下手婚配,a固然没法和(m+)婚配乐成了,以是间接就前往nil了。这个处置在下面讲string.find的时分源码函数str_find_aux()里else分支形式婚配里能够看到,有专门处置^字符的代码。
那末在string.gmatch函数中,假如把^放在形式的后面的意义甚么呢?它的意义是不举行婚配了,也就是间接前往,注重不是前往nil,而是函数间接前往,栈上没有任何前往值。
老模样,固然能够设想失掉string.gmatch()的完成应当跟下面的差未几,但仍是看一眼源码对照保险:
后面那两行是前往Lua迭代器所需求的形态和迭代函数,不必往管它。让我们来看一下gmatch_aux(lua_State*L)函数,刨往为了迭代器做处置以后,就和string.match()函数完成没有甚么区分了,最初都挪用match()函数举行形式婚配。分歧的中央就是下面说的字符^的处置这里是没有的。
4、string.gsub(s,pattern,rep,n)
这个函数跟string.gmatch()一样,也带一个g,能够设想失掉,这个函数也会取得一切的婚配字符串,而不像string.find()和string.match()一样,碰着一个就停止了。的确是如许。这个函数的感化是在字符串s中查找与形式pattern相婚配的一切串,然后用rep参数发生的字符串举行交换,你大概要问,为何是rep发生的串,而不是rep本人呢?由于这里的rep除能够是一个字符串外,还能够是一个函数,乃至能够是一个table。
当rep是一个字符串的时分,一样平常来讲是看成一个一般的字符串来处置,也就是间接用这个字符串来交换婚配到的串,可是这里会特别的处置标记%,%前面接数字1-9的时分,也就是用后面形式捕捉的序号1-9对应的字符串来交换形式婚配的内容,如许说对照绕,仍是看例子:
locals="am+dmf"
print(string.gsub(s,"()(m+)","%1"))--a2+d5f2
print(string.gsub(s,"()(m+)","%2"))--am+dmf2
print(string.gsub(s,"()(m+)","%3"))--error:invalidcaptureindex
下面我们用的形式是()(m+),这个形式会有2个捕捉,分离是字符串以后的地位和m+,string.gsub()婚配到的第一个中央是am+dmf,这个时分两个捕捉分离是2,m,那末%1也就是第一个捕捉,也就是2,交换后的串为a2+dmf,接着又婚配到第二个中央am+dmf,这里的两个捕捉分离是5,m,那末%1指向的第一个捕捉是5,交换后的串为a2+d5f,这就是了局显现的内容。前面谁人数字2的意义是交换乐成了2次。依据下面的剖析就不难了解,为何用%2往交换的时分字符串没有变,由于原本就是用m往交换m,固然稳定。别的,第三个print()会报错,由于只要2个捕捉,而你要往利用%3,那末天然就没有这个捕捉了。
这里大概还必要注重的中央就是%只会和前面紧接着的数字分离,换句话说为何后面要说是1-9就是这个缘故原由,固然捕捉能够默许到达之前说的32个,可是只能用前9个了。有一个对照特别的是%0,它是用婚配到的串往交换,复杂来讲就是反复婚配到的串,好比如许:
locals="am+dmf"
print(string.gsub(s,"()(m+)","%0%0%0"))--ammm+dmmmf2
婚配到的串是m,用mmm交换了原串中的m。
你大概要问,既然%被独自处置了,那末我想要用%往交换怎样办,只必要用%%就能够暗示%本身了。好比:
locals="am+dmf"
print(string.gsub(s,"()(m+)","%%"))--a%+d%f2
当rep是一个table的时分,每次婚配到了以后,城市用第一个捕捉作为key往查询这个table,然后用table的内容来交换婚配串,假如没有指定捕捉,那末,就用全部婚配串作为key往查询,假如没有查到对应key的值,大概对应的值不是字符串和数字,那末就不做交换:
locals="am+dmf"
localt1={
[2]="hh",
[5]="xx",
}
localt2={}
print(string.gsub(s,"()(m+)",t1))--ahh+dxxf2
print(string.gsub(s,"()(m+)",t2))--am+dmf2
localt3={
[2]=false
}
print(string.gsub(s,"()(m+)",t3))--am+dmf2
localt4={
[2]={123}
}
print(string.gsub(s,"()(m+)",t4))--error:invalidreplacementvalue(atable)
当rep是一个函数的时分,每当婚配到字符串的时分,就把形式一切的捕捉依照捕捉按次作为参数传送给这个函数,假如没有指定捕捉,则传送全部婚配的字符串给函数,函数的前往值假如是字符串大概是数字就交换失落婚配,假如不是则不做交换:
locals="am+dmf"
functionf1(...)
print(...)--2m--5m
return"hh"
end
functionf2()
return{123}
end
print(string.gsub(s,"()(m+)",f1))--ahh+dhhf2
print(string.gsub(s,"()(m+)",f2))--error:invalidreplacementvalue(atable)
第四个参数,用来标明,必要交换到第几个婚配为止,好比:
locals="am+dmf"
print(string.gsub(s,"()(m+)","%%",-1))--am+dmf0
print(string.gsub(s,"()(m+)","%%",0))--am+dmf0
print(string.gsub(s,"()(m+)","%%",1))--a%+dmf1
print(string.gsub(s,"()(m+)","%%",2))--a%+d%f2
print(string.gsub(s,"()(m+)","%%",3))--a%+d%f2
仍然来看看源码是怎样写的:
staticintstr_gsub(lua_State*L){
size_tsrcl;
constchar*src=luaL_checklstring(L,1,&srcl);
constchar*p=luaL_checkstring(L,2);
intmax_s=luaL_optint(L,4,srcl+1);
intanchor=(*p==^)?(p++,1):0;
intn=0;
MatchStatems;
luaL_Bufferb;
luaL_buffinit(L,&b);
ms.L=L;
ms.src_init=src;
ms.src_end=src+srcl;
while(n<max_s){
constchar*e;
ms.level=0;
e=match(&ms,src,p);
if(e){
n++;
add_value(&ms,&b,src,e);
}
if(e&&e>src)/*nonemptymatch?*/
src=e;/*skipit*/
elseif(src<ms.src_end)
luaL_addchar(&b,*src++);
elsebreak;
if(anchor)break;
}
luaL_addlstring(&b,src,ms.src_end-src);
luaL_pushresult(&b);
lua_pushinteger(L,n);/*numberofsubstitutions*/
return2;
}
能够看到它处置了标记^,轮回举行婚配,假如婚配到了,就依照分歧的范例把交换串增加进了局里,最初把一切字符压回栈上。
总的来讲string.gsub()函数完成的效果跟我们一样平常意义上的交换是不异的,你大概会烦闷为何它不叫string.greplace,实在我也烦闷。
下面先容完了4个用到了形式的函数以后,我们再来看看Lua的形式有甚么奇奥的地方。
形式
让我们来看看,都有哪些特别字符必要注释,实在这一局部在Lua的官方文档中,先容的仍是很分明的:
起首,任何独自的字符,除下面那些特别字符外,都代表他们自己。注重条件是他们自力呈现。
其次,Lua界说了一些汇合,它们分离以下:
.:代表恣意的字符。
%a:代表恣意字母。
%c:代表恣意把持字符。
%d:代表恣意数字。
%l:代表恣意小写字母。
%p:代表恣意标点标记。
%s:代表恣意空缺字符(好比空格,tab啊)。
%u:代表恣意年夜写字母。
%w:代表恣意字母和数字。
%x:代表恣意16进制数字。
%z:代表恣意跟0相称的字符。
%前面跟恣意一个非字母和数字的字符,都代表了这个字符自己,包含下面那些特别字符和任何标点标记都能够用这个体例来表达。
[set]:代表一个自界说的字符汇合。你可使用标记-来标识一个局限,好比1-9,a-z之类的。必要注重的是,下面提到的那些字符汇合也能够在这个自界说的汇合里用,可是你不克不及这么写[%a-z],如许的汇合是没成心义的。
[^set]:代表字符汇合[set]的补集(补集是甚么意义,我了个往,问你数学先生往)。
别的,关于下面提到的一切用%跟一个字母构成的汇合,假如把字母年夜写,那末就对应谁人汇合的补集,好比%S的意义就是一切非空缺字符。Lua官网还夸大了一下,这里个界说跟当地的字符集有关,好比汇合[a-z]就纷歧定跟%l是相称的。
恣意一个单字符表达的汇合,包含%加单字符表达的汇合前面都能够跟4种标记,他们分离是*、+、-、?。
*:意义是后面的汇合婚配0个大概更多字符,而且是只管多的婚配。
+:意义是后面的汇合婚配1个大概更多字符。
-:意义是后面的汇合婚配0个大概更多字符,只管少的婚配。
?:意义是后面的汇合婚配0个大概1个。
以下:
locala="ammmf"
print(string.match(a,"%a"))--a
print(string.match(a,"%a*"))--ammmf
print(string.match(a,"%a+"))--ammmf
print(string.match(a,"%a-"))--
print(string.match(a,"%a?"))--a
看了下面的例子,你大概会想,那*和+大概加不加?有甚么区分呢?是有区分的,由于婚配0个和婚配1个有的时分就是有无婚配乐成的关头,好比加上?就能够婚配0个,意味着即便没有对应汇合的内容,也算婚配乐成了,假如有捕捉的话,这个时分捕捉是失效的。好比:
locala="ammmf"
print(string.match(a,"()c"))--nil
print(string.match(a,"()c?"))--1
假如你如今还不晓得string.match()是甚么意义,就翻到后面往看吧。
另有一个特别的字符必要先容,就是%b前面跟两个分歧的字符xy,它的意义是婚配从x入手下手,到y停止的字符串,并且请求这个字符串里x和y的数目要不异。好比%b()就是婚配一般的小括号,以下:
locala="aaabb"
print(string.match(a,"%bab"))--aabb
最初,我在先容string.gmatch的时分先容过字符^的用法,它放在形式的首部,意义是从原串的首部就入手下手婚配,这里另有一个特别字符跟它的用法相似,它就是$字符,这个字符放在形式的开端,意义是从原串的尾部入手下手婚配。在其他地位就跟^一样,也没成心义。
捕捉
捕捉的意义在先容string.find的时分已具体先容过了,这里再提一笔,捕捉是在形式中,用小括号括起来的子形式,它在婚配产生的时分截取小括号内形式婚配到的字符串,然后保留上去,默许最多保留32个,能够在Lua源码中修正保留的数目。别的捕捉的按次是依照小括号左括号的地位来定的。至于捕捉怎样利用,请参看我下面先容的4个利用了形式的函数的详细用法。
本文出自“菜鸟浮出水”博客,请务必保存此出处http://rangercyh.blog.51cto.com/1444712/1393067
如果您觉得本篇CentOSLinux教程讲得好,请记得点击右边漂浮的分享程序,把好文章分享给你的好朋友们! |
|