Lua 字符串处理

今天项目中遇到一个字符串处理的通用函数,一事不明白胡乱搜索后才发现原来是字符串处理库里面的通用函数,想着该理一遍字符串。

这个库提供了字符串处理的通用函数。例如字符串查找,子串,模式匹配等。当在Lua中对字符串做索引时,第一个字符从1开始计算(而不是C里的0)。索引可以是负数。它指从字符串末尾反向解析。即,最后一个字符在-1位置处,等等。

字符串库中的所有函数都在表string中,它还将其设置为字符串元表的__index域。因此,你可以7以面向对象的形式使用字符串函数。例如,string.byte(s,i)可以写成s:byte(i)

字符串或串(String)是由数字,字母,下划线组成的一串字符。

Lua语言中字符串可以使用以下三种方式来表示:

(1)单引号间的一串字符。

(2)双引号间的一串字符。

(3)[[和]]间的一串字符。

以下三种方法的字符串实例如下:

string1 = "字符串1"
string2 = '字符串2'
string3 = [["字符串3"]]

转义字符用于表示不能直接显示的字符,比如后退键,回车键,等。如在字符串转换双引号可以使用"\"" 。

所有的转义字符和所对应的意义:

转义字符

意义

ASCII码值(十进制)

\a

响铃(BEL)

007

\b

退格(BS) ,将当前位置移到前一列

008

\f

换页(FF),将当前位置移到下页开头

012

\n

换行(LF) ,将当前位置移到下一行开头

010

\r

回车(CR) ,将当前位置移到本行开头

013

\t

水平制表(HT) (跳到下一个TAB位置)

009

\v

垂直制表(VT)

011

\\

代表一个反斜线字符''\'

092

\'

代表一个单引号(撇号)字符

039

\"

代表一个双引号字符

034

\0

空字符(NULL)

000

\ddd

1到3位八进制数所代表的任意字符

三位八进制

\xhh

1到2位十六进制所代表的任意字符

二位十六进制

字符串操作

Lua提供了很多的方法来支持字符串的操作:

字符串库假定采用单字节符编码。

1.string.byte(s[,i[,j]])

返回字符s[i],s[i+1],...,s[j]的内部数字编码。i的默认值是1;j的的默认值是i。这些索引以函数string.sub的规则修正。

local str = "012abcd"
print(string.byte(str,1,4))

输出结果为48 49 50 97

数字编码没有必要跨平台。

2.string.char(...)

接收零或更多的整数。返回和参数数量相同长度的字符串。其中每个字符的内部编码。其中每个字符的内部编码值等于对应的参数值。

char 将整型数字转成字符比连接,byte转换字符为整数值(可以指定某个字符,默认第一个字符)。

string.char(97,98,99,100)
-- a b c d
string.byte("ABCD",4)
--68
string.byte("ABCD")
--65

3.string.dump(function[,strip])

返回包含有以二进制方式表示的(一个二进制代码块)指定函数的字符串。之后可以用load调用这个字符串获得该函数的副本(但是绑定新的上值)。如果strip为真值。二进制代码块不携带该函数的调试信息(局部变量名,行号,等等。)。

带上值得函数只保存上值得数目。当(再次)加载时,这些上值被更新为nil的实例。(你可以使用调试库按你需要的方式来序列化上值,并重载到函数中)

首先新建一个文件将文件命名为Test.lua然后编辑如下代码:

--自定义一个函数
function custom_func(num1, num2)
    local ret = num1 + num2;
    print("\nnum1 = "..num1)
    print("num2 = "..num2)
    print("num1 + num2 = "..ret)
end

-- 将函数序列化
local func_content = string.dump(custom_func)
print("\nfunc_content = "..func_content)

-- 将转化后的字符串写入文件
local outfile = io.open("dumptest.txt", "w")
local outnum = outfile:write(func_content)
outfile:close()

-- 从文件总读取内容
local infile = io.open("dumptest.txt", "r")
local incontent = infile:read("*a")
infile:close()
print("\ninput content is "..incontent)

-- 加载函数
local myfunc = loadstring(incontent)

-- 执行函数
myfunc(1, 1)

myfunc(3, 6)

print(os.date())

运行结果如下

coderdeMac-mini:Desktop yingyingzhu$ lua Test.lua

func_content = u�R

�@�@A@A�@�@�@�A�@�@�@A�@�print
num1 =num2 = num1 + num2 =
@Test.luanum1num2ret_ENV

input content is u�R

�@�@A@A�@�@�@�A�@�@�@A�@�print
num1 =num2 = num1 + num2 =
@Test.luanum1num2ret_ENV

num1 = 1
num2 = 1
num1 + num2 = 2

num1 = 3
num2 = 6
num1 + num2 = 9
Thu Jul 18 19:43:05 2019
coderdeMac-mini:Desktop yingyingzhu$

调用了函数string.dump()之后函数custom_func()被转化为字符串保存在文件中,可以在使用时再取出来。这个函数一般在写逻辑代码应该用不到。更多的是做框架的时候用的功能,进过序列化的函数可以通过网络传送,转化,再使用。

4.string.find(s,pattern[,init[,plain]])

查找第一个字符串s中匹配到的 pattern。如果找到一个匹配,find会返回s中关于它起始位置以及终点位置的索引;否则,返回nil。第三个可选数字参数init指明从哪里搜索;默认值为1,同时可以是负值。第四个可选参数为true时,关闭模式匹配机制。此时函数仅做直接的“查找子串”的操作,而pattern中没有字符被看作魔法字符。注意,如果给定了plain,就必须写上init。

如果在模式中定义了捕获,捕获到的若干值也会在两个索引之后返回。

string.find("Hello lua user","lua",1)

输出为7 ,9

5.string.format(formatstring,...)

返回不定数量参数的格式化版本,格式化串为第一个参数(必须是一个字符串)。格式化字符串遵循ISO C函数sprintf的规则。不同点在于选项*,h,L,l,n,p 不支持,另外还增加了一个选项q。q选项将一个字符串格式化为两个双引号括起,对内部字符做恰当的转义处理的字符串。该字符串可以安全的被Lua解释器读出来。例如,调用

string.format('%q', 'a string with "quotes" and \n new line')

会产生字符串:

"a string with \"quotes\" and \

new line"
string.format("the value is:%d",4)
the value is:4

5.string.gmatch(s,pattern)

返回一个迭代器函数。每次调用这个函数都会以pattern对s做匹配,并返回所有捕获到的值。如果pattern中没有指定捕获,则每次捕获整个pattern.

下面这个例子会循环迭代字符串s中所有的单词,并逐行打印:

s = "hello world from Lua"
for w in string.gmatch(s,"%a+") do
	print(w)
end 

下一个例子从指定的字符串中收集所有的键值对key = value置入一张表:

s = "hello world from Lua"
for w in string.gmatch(s,"%a+") do
	print(w)
end 

6.string.gsub(s,pattern,repl[,n])

将字符串s中,所有的(或是在n给出时的前n个) pattern都替换成repl,并返回其副本。repl可以是字符串、表,或函数。gsub还会在第二个返回值返回一共发生多少次匹配。gsub这个名字来源于Global SUBstitution。

如果repl是一个自付出,那么把这个自妇产作为替换品。字符%是一个转义符:repl中的所有形式为%d的串表示第d 个捕获到的子串,d可以是1到9。串%0表示整个匹配,串%%表示单个%。

如果repl是张表,每次匹配时都会用第一个捕获物作为键去查这张表。

如果repl是个函数u,则在每次匹配发生时都会调用这个函数。所有捕获到的子串依次作为参数传入。

任何情况下,模板中没有设定捕获都看成是捕获这个模板。

如果表的查询结果或函数的返回结果是一个字符串或是个数字,都将其作为替换用串;而在返回false 或nil时不作替换(即保留匹配前的原始串)。

--常规替换
x = string.gsub("hello world","(%w+)","lua")
print("1.",x)

--都有匹配的第一个串*2来替换
x = string.gsub("hello world","(%w+)","%1 %1")
print("2.",x)

--用匹配的完成串*2来替换第一次匹配的结果
x = string.gsub("hello world","%w+","%0 %0",1)
print("3.",x)
x = string.gsub("hello world","%w+","%0 %0")
print("3.1",x)
x = string.gsub("hello world","%w+","%0")
print("3.2",x)
x = string.gsub("hello world","%w+","%0%0")
print("3.3",x)
x = string.gsub("hello world","%w+","%0",1)
print("3.4",x)
x = string.gsub("hello world","%w+","%0 %0 %0",1)
print("3.5",x)
x = string.gsub("hello world","%w+","%0 %0 %0 %0",1)
print("3.6",x)


--使用一个完整匹配和一个匹配的第二个串来替换
x = string.gsub("os = $OS,pathext = $PATHEXT","%$(%w+)",os.getenv)
print("4.",x)

--调用自定义函数
x = string.gsub("4 + 5 = $return 4 + 5$","$(.-)%$",function(s)
	return loadstring(s)()
	end)
print("5.",x)

--调用表来替换
local t = {name = "lua",version = "5.1"}
x = string.gsub("$name - $version.tar.gz","%$(%w+)",t)
print("6.",x)

第三个部分不容易理解,所以我这边写了很多测试函数,运行结果如下

coderdeMac-mini:Desktop yingyingzhu$ lua Test.lua
1.	lua lua
2.	hello hello world world
3.	hello hello world
3.1	hello hello world world
3.2	hello world
3.3	hellohello worldworld
3.4	hello world
3.5	hello hello hello world
3.6	hello hello hello hello world
4.	os = $OS,pathext = $PATHEXT
5.	4 + 5 = 9
6.	lua - 5.1.tar.gz

7.string.len(s)

接收一个字符串,返回其长度。空串""的长度为0。内嵌零也统计在内,因此"a\000bc\000"的长度为5

string.len("abc")

 结果为   3

8.string.lower(s)

接收一个字符串,将其中的大写字符都转为小写后返回其副本。其它的字符串不会更改。对大写字符的定义取决于当前的区域设置。

9.string.match(s,pattern[,init])

在字符串s中找到第一个能用pattern匹配到部分。如果能找到,match返回其中的捕获物;否则返回nil。如果pattern中未指定捕获,返回整个pattern捕获到的串。第三个可选数字参数init指明从哪里开始搜索;它默认为1且可以是负数。

string.match("I have 2 questions for you.","%d+ %a+")
print(string.match("I have 2 questions for you.","%d+ %a+"))
string.match("I have 2 questions for you.","%a+ %d+")
print(string.match("I have 2 questions for you.","%a+ %d+"))

 

coderdeMac-mini:Desktop yingyingzhu$ lua Test.lua
2 questions
have 2

10.string.pack(fmt,v1,v2,...)

返回一个打包了(即以二进制形式序列化)v1,v2等值得二进制字符串。字符串fmt为打包格式。

11.string.packsize(fmt)

返回以指定格式用string.pack打包的字符串的长度。格式字符串中不可以有变长选项's'或'z'。

12.string.rep(s,n[,sep])

返回n个字符串s以字符串sep为分割符连在一起的字符串。默认的sep值为空字符串(即没有分割符)。如果n不是正数则返回空串。

string.rep("abcd",2)

输出为 abcdabcd

13.string.reverse(s)

返回字符串s的翻转串。

14.string.sub(s,i[,j])

返回s的子串,该子串从i 开始到j为止;i和j都可以为负数。如果不给出j,就当它是-1(和字符串长度相同)。特别是,调用string.sub(s,1,j)可以返回s的长度为j的前缀串,而string.sub(s,-i)返回长度i的后缀串。

如果在对负数索引转义后小于1的话,就修正回1。如果j比字符串的长度还大,就修正为字符串长度。如果在修正之后,i大于j,函数返回空串。

15.string.unpack(fmt,s[,pos])

返回以格式fmt打包在字符串s中的值。选项pos(默认为1)标记了从s中哪里开始读起。读完所有的值后,函数返回s中第一个未读字节的位置。

16.string.upper(s)

接收一个字符串,将其中的小写字符都转为大写后返回其副本。其它的字符串不会更改。对小写字符的定义取决于当前的区域设置。

匹配模式

Lua中的匹配模式直接用常规的字符串来描述。它用于模式匹配函数string.find,string.gmath,string.gsub,string.match。这一节表述了这些字符串的语法及含义(即它能匹配到什么)。

字符类:

字符类用于表示一个字符集合。下列组合可用于字符类:

x: (这里 x 不能是 魔法字符 ^$()%.[]*+-? 中的一员) 表示字符 x 自身。
.: (一个点)可表示任何字符。
%a: 表示任何字母。
%c: 表示任何控制字符。
%d: 表示任何数字。
%g: 表示任何除空白符外的可打印字符。
%l: 表示所有小写字母。
%p: 表示所有标点符号。
%s: 表示所有空白字符。
%u: 表示所有大写字母。
%w: 表示所有字母及数字。
%x: 表示所有 16 进制数字符号。
%x: (这里的 x 是任意非字母或数字的字符) 表示字符 x。 这是对魔法字符转义的标准方法。 所有非字母或数字的字符 (包括所有标点,也包括非魔法字符) 都可以用前置一个 '%' 放在模式串中表示自身。
[set]: 表示 set 中所有字符的联合。 可以以 '-' 连接,升序书写范围两端的字符来表示一个范围的字符集。 上面提到的 %x 形式也可以在 set 中使用 表示其中的一个元素。 其它出现在 set 中的字符则代表它们自己。 例如,[%w_] (或 [_%w]) 表示所有的字母数字加下划线), [0-7] 表示 8 进制数字, [0-7%l%-] 表示 8 进制数字加小写字母与 '-' 字符。
交叉使用类和范围的行为未定义。 因此,像 [%a-z] 或 [a-%%] 这样的模式串没有意义。

[^set]: 表示 set 的补集, 其中 set 如上面的解释。

所有单个字母表示的类别(%a,%c,等),若将其字母改为大写,均表示对应的不急。例如,%S表示所有非空格的字符。

如何定义字母、空格、或是其他字符组取决于当前的区域设置。特别注意:[a-z]未必等价于%l。

模式条目:

模式条目 可以是

  • 单个字符类匹配该类别中任意单个字符;
  • 单个字符类跟一个 '*', 将匹配零或多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 '+', 将匹配一或更多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 '-', 将匹配零或更多个该类的字符。 和 '*' 不同, 这个条目总是匹配尽可能短的串;
  • 单个字符类跟一个 '?', 将匹配零或一个该类的字符。 只要有可能,它会匹配一个;
  • %n, 这里的 n 可以从 1 到 9; 这个条目匹配一个等于 n 号捕获物(后面有描述)的子串。
  • %bxy, 这里的 x 和 y 是两个明确的字符; 这个条目匹配以 x 开始 y 结束, 且其中 x 和 y 保持 平衡 的字符串。 意思是,如果从左到右读这个字符串,对每次读到一个 x就 +1 ,读到一个 y 就 -1, 最终结束处的那个 y 是第一个记数到 0 的 y。 举个例子,条目 %b() 可以匹配到括号平衡的表达式。
  • %f[set], 指 边境模式; 这个条目会匹配到一个位于 set 内某个字符之前的一个空串, 且这个位置的前一个字符不属于 set 。 集合 set 的含义如前面所述。 匹配出的那个空串之开始和结束点的计算就看成该处有个字符 '\0' 一样。

模式:

模式 指一个模式条目的序列。 在模式最前面加上符号 '^' 将锚定从字符串的开始处做匹配。 在模式最后面加上符号 '$' 将使匹配过程锚定到字符串的结尾。 如果 '^' 和 '$' 出现在其它位置,它们均没有特殊含义,只表示自身。

捕获:

模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物。 当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。 捕获物以它们左括号的次序来编号。 例如,对于模式 "(a*(.)%w(%s*))" , 字符串中匹配到 "a*(.)%w(%s*)" 的部分保存在第一个捕获物中 (因此是编号 1 ); 由 "." 匹配到的字符是 2 号捕获物, 匹配到 "%s*" 的那部分是 3 号。

作为一个特例,空的捕获 () 将捕获到当前字符串的位置(它是一个数字)。 例如,如果将模式 "()aa()" 作用到字符串 "flaaap" 上,将产生两个捕获物: 3 和 5 。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值