字符串标准库提供了基于模式的4个函数。

string.find 指定目标字符串中搜索指定的模式,找到模式后返回模式开始位置索引和结束位置的索引,没有匹配则返回nil;后两个参数可选,第三个为开始索引的位置,第四个为是否进行简单搜索。

string.match 返回目标字符串中与模式相匹配的子串。

string.gsub 将目标字符串中的所有出现的模式替换成字符串,可以通过第四个参数限制替换次数;返回替换完的字符串和发生替换的次数。

string.gmatch 遍历一个字符中所有出现的指定模式。

模式用到的魔法字符 ( ) . % + - * ? [ ] ^ $

format中的转义字符有效的同时,上面魔法字符通过前加 % 转义,如 %. 表示 .

预置字符分类 (大写形式表示类的补集)

.

任意字符

%a

字母

%c

控制字符

%d

数字

%g

除空格外的可打印字符

%l

小写字母

%p

标点符号

%s

空白字符

%u

大写字母

%w

字母与数字

%x

十六进制数字

使用字符集可创建自定义的字符分类,利用 [ ] 括住所需要的字符即可;通过a-z可以表示连续的许多字符;^ 可以表示当前字符集的补集。

在模式中可以对单个字符或字符分类进行匹配修饰。(模式修饰符)

+

重复一次或多次

*

重复零次或多次

-

重复零次或多次(最小匹配)

?

出现零次或一次

模式以 ^ 开头表示必须从目标字符串开头匹配,以 $ 结尾表示匹配必须到目标字符串的结尾。

模式 "%bxy" 表示匹配成对的字符串,以x起始,到y结束。

模式 "%f[char-set]" 表示只有在后一个字符位于 char-set 内而前一个字符不在时匹配一个空字符串。

模式具有捕获机制,允许根据一个模式从目标字符串中抽出与该模式相匹配的内容。

string.match 会将所有捕获的值作为单独结果返回。

模式 %n 表示匹配第 n 个捕获的副本;%0 表示整个匹配

string.gsub 第三个参数可以是表、函数;当参数是表时,将表第一个捕获的内容作为表的键,表中对应键值作为替换字符串,如果表不存在这个键或键值为 nil 则不改变此匹配;当参数是函数时,参数是当前匹配捕获到的内容,返回值作为替换的字符串,当函数返回值是 nil 时不改变这个匹配。

 

 

感觉Lua的模式并不复杂,可以完成许多字符串处理和提取信息;但复杂的模式并不易于阅读,也容易出错,模式修饰符匹配的具体个数还需要实际测试,尤其是一个模式中使用了许多个模式修饰符的时候,很容易拿不准出错。。

练习10.1

--exercise10.1
function spilt(str,mode)
  local tab,cnt={},1
  for i in string.gmatch(str,string.format(".-%%f[%s]",mode,mode)) do
    tab[cnt]=i
    cnt=cnt+1
  end
  for i in string.gmatch(str,string.format("[^%s]+$",mode)) do
    tab[cnt]=i
    cnt=cnt+1
  end
  return tab
end
print(table.unpack(spilt("what asd . + whole new world as ","+")))

练习10.2

否,两个互补接触的子集的补集之和为1,'[%D%U]' 表示全部字符。

练习10.3

--exercise10.3
function transliterate(str,tab)
  for i in pairs(tab) do
    local sp=string.gsub(i,"(%W)","%%%1")
    if tab[i]==false then
      str=string.gsub(str,sp,"")
    else
      --tab[i]=string.gsub(tab[i],"(%W)","%%%1")
      str=string.gsub(str,sp,tab[i])
    end
  end
  return str
end
b={a=1,b="123",s=false,["["]="]"}
print(transliterate("adsab[[[]abg",b))

模式中的魔法字符需要先加%处理。

练习10.4

--exercise10.4
function trim(s)
  s=string.match(s,"%S.*%S")
  return s
end
strs=string.rep(" ",102400)
time=os.clock()
trim(" 1"..strs.."1 ")
print(os.clock()-time)

当中间字符存在大量空白字符时,原trim会耗费O(n^2)时间复杂度,如首尾为 1 ,中间102400个空格,i7-4710笔记本跑284.726s。

采用match直接找到非空白字符开头和结尾的最长子串即可。

练习10.5

--exercise10.5
function escape(str)
  local cnt=1
  str=string.gsub(str,"(.)",function(f)
    if cnt<10 then
      cnt=cnt+1
      return string.format("\\x%02X",string.byte(f))
    else
      cnt=1
      return string.format("\\x%02X\\z\n",string.byte(f))
    end
  end)
  return str
end
print(escape("\0\1hello\200abcdefghijklm\n\a\tnopqrstuvwxyz"))

逐字符转换,每转换10个字符输出 \z 换行一次。

练习10.6

--exercise10.6
function transliterate(str,tab)
  str=string.gsub(str,"(.)",function(f)
    return "\\"..utf8.codepoint(f)
  end)
  --print("1>>",str)
  for i in pairs(tab) do
    --local sp=string.gsub(i,"(%W)","%%%1")
    local sp=string.gsub(i,"(.)",function(f)
      return "\\"..utf8.codepoint(f)
    end)
    --print("2>>",sp)
    if tab[i]==false then
      str=string.gsub(str,sp,"")
    else
      tab[i]=string.gsub(tab[i],"(.)",function(f)
        return "\\"..utf8.codepoint(f)
      end)
      str=string.gsub(str,sp,tab[i])
    end
  end
  --print("3>>",str)
  str=string.gsub(str,"\\(%d+)",function(f)
    return utf8.char(f)
  end)
  --print("4>>",str)
  return str
end
b={a=1,b="123",s=false,['+']='qq'}
print(transliterate("bsss+s+a",b))

全部转换成utf8的数字码,替换处理后转为utf8字符。

练习10.7

--exercise10.7
function utf8reverse(str)
  local utftab,i={},1
  str=string.gsub(str,"(.)",function(f)
    utftab[i]=utf8.codepoint(f)
    i=i+1
    return "\\"..utf8.codepoint(f)
  end)
  str=string.gsub(str,"\\(%d+)",function(f)
    i=i-1
    return utf8.char(utftab[i])
  end)
  return str
end
--print(utf8reverse("bsss+s+a"))

逐字符转换成utf8数字码的同时用序列存储起来,遍历数字码时,从尾到头输出utf8字符。

END