String 库(上)

Posted heyuchang666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了String 库(上)相关的知识,希望对你有一定的参考价值。

Lua_第19String

      Lua解释器对字符串的支持很有限。一个程序可以创建字符串并连接字符串,但不能截取子串,检查字符串的大小,检测字符串的内容。在 Lua中操纵字符串的功能基本来自于 string 库。

      String 库中的一些函数是非常简单的:string.len(s)返回字符串 s 的长度;string.rep(s, n)返回重复 n 次字符串 s 的串;你使用 string.rep("a", 2^20)可以创建一个 1M bytes 的字符 串(比如,为了测试需要);string.lower(s)将 s 中的大写字母转换成小写(string.upper 将小写转换成大写)。如果你想不关心大小写对一个数组进行排序的话,你可以这样:

table.sort(a, function (a, b)
return string.lower(a) < string.lower(b)
end)

       string.upper 和 string.lower都依赖于本地环境变量。所以,如果你在 European Latin-1环境下,表达式:

string.upper("a??o")
--> "A??O".

    调用 string.sub(s,i,j)函数截取字符串 s 的从第 i 个字符到第 j 个字符之间的串。Lua 中,字符串的第一个字符索引从 1开始。你也可以使用负索引,负索引从字符串的结尾 向前计数:-1 指向最后一个字符,-2 指向倒数第二个,以此类推。所以,string.sub(s, 1,j)返回字符串 s 的长度为 j 的前缀;string.sub(s, j, -1)返回从第 j 个字符开始的后缀。如果不提供第 3 个参数,默认为-1,因此我们将最后一个调用写为 string.sub(s, j);string.sub(s, 2, -2)返回去除第一个和最后一个字符后的子串。

s = "[in brackets]"
print(string.sub(s, 2,-2))     --> inbrackets

记住:Lua 中的字符串是恒定不变的。String.sub 函数以及 Lua中其他的字符串操作函数都不会改变字符串的值,而是返回一个新的字符串。一个常见的错误是:

string.sub(s, 2,-2)<span style="font-family: 'Microsoft YaHei'; background-color: rgb(255, 255, 255);"> </span>

认为上面的这个函数会改变字符串 s 的值。如果你想修改一个字符串变量的值,你 必须将变量赋给一个新的字符串:

s =string.sub(s, 2, -2)

string.char 函数和 string.byte 函数用来将字符在字符和数字之间转换。string.char 获 取 0个或多个整数,将每一个数字转换成字符,然后返回一个所有这些字符连接起来的 字符串。string.byte(s,  i)将字符串 s 的第 i 个字符的转换成整数;第二个参数是可选的,缺省情况下 i=1。下面的例子中,我们假定字符用 ASCII 表示:

<pre name="code" class="csharp">print(string.char(97))                    --> a 
i = 99;print(string.char(i, i+1, i+2))           --> cde 
print(string.byte("abc"))                 --> 97
print(string.byte("abc", 2))              --> 98
print(string.byte("abc", -1))             --> 99

      上面最后一行,我们使用负数索引访问字符串的最后一个字符。

     函数 string.format 在用来对字符串进行格式化的时候,特别是字符串输出,是功能强大的工具。这个函数有两个参数,使用和 C 语言的 printf 函数几乎一模一样,你完全 可以照 C 语言的 printf 来使用这个函数。第一个参数为格式化串:由指示符和控制格式 的字符组成。指示符后的控制格式的字符可以为:十进制‘d‘;十六进制‘x‘;八进制‘o‘; 浮点数‘f‘;字符串‘s‘。在指示符‘%‘和控制格式字符之间还可以有其他的选项:用来控制 更详细的格式,比如一个浮点数的小数的位数:

 
print(string.format("pi =%.4f", PI))
    --> pi =3.1416
d = 5; m = 11; y =1990
print(string.format("%02d/%02d/%04d", d, m, y))
    --> 05/11/1990
tag, title= "h1", "a title"
print(string.format("<%s>%s</%s>", tag,title, tag))
    --> <h1>a title</h1>

第一个例子,%.4f 代表小数点后面有 4 位小数的浮点数。第二个例子%02d 代表以固定的两位显示十进制数,不足的前面补 0。而%2d前面没有指定 0,不足两位时会以 空臼补足。对于格式串部分指示符得详细描述清参考lua 手册,或者参考 C 手册,因为 Lua 调用标准 C 的printf 函数来实现最终的功能。

19.1 模式匹配函数

       在string库中功能最强大的函数是:string.find(字符串查找),string.gsub(全局字符串替换),and    string.gfind(全局字符串查找)。这些函数都是基于模式匹配的。

       与其他脚本语言不同的是,Lua并不使用POSIX(POSIX是unix的工业标准,regexp最初来源于unix,POSIX对regexp也作了规范。)规范的正则表达式(也写作regexp) 来进行模式匹配。主要的原因出于程序大小方面的考虑:实现一个典型的符合POSIX标 准的regexp大概需要 4000  行代码,这比整个Lua标准库加在一起都大。权衡之下,Lua中的模式匹配的实现只用了 500 行代码,当然这意味着不可能实现POSIX所规范的所有 更能。然而,Lua中的模式匹配功能是很强大的,并且包含了一些使用标准POSIX模式匹配不容易实现的功能。

       string.find  的基本应用就是用来在目标串(subject  string)内搜索匹配指定的模式的 串。函数如果找到匹配的串返回他的位置,否则返回 nil.最简单的模式就是一个单词,仅仅匹配单词本身。比如,模式‘hello‘仅仅匹配目标串中的"hello"。当查找到模式的时候, 函数返回两个值:匹配串开始索引和结束索引。

<pre name="code" class="csharp"><pre name="code" class="csharp">s = "hello world"
i, j = string.find(s, "hello")
print(i, j)                        --> 1   5
print(string.sub(s, i, j))         --> hello 
print(string.find(s, "world"))                          --> 7   11
i, j =string.find(s, "l")
print(i, j)                        --> 3   3
print(string.find(s,"lll"))       --> nil


      例子中,匹配成功的时候,string.sub 利用 string.find返回的值截取匹配的子串。(对简单模式而言,匹配的就是其本身)

     string.find 函数第三个参数是可选的:标示目标串中搜索的起始位置。当我们想查找 目标串中所有匹配的子串的时候,这个选项非常有用。我们可以不断的循环搜索,每一 次从前一次匹配的结束位置开始。下面看一个例子,下面的代码用一个字符串中所有的新行构造一个表:

local t = {}      -- table tostore the indices
local i = 0
while true do
   i = string.find(s, "\n", i+1)   -- find'next' newline
   if i == nil then breakend
   table.insert(t, i)
end

       后面我们还会看到可以使用 string.gfind 迭代子来简化上面这个循环。

     string.gsub    函数有三个参数:目标串,模式串,替换串。他基本作用是用来查找匹配模式的串,并将使用替换串其替换掉:

s = string.gsub("Lua iscute", "cute", "great") print(s)  --> Lua is great
s = string.gsub("all lii", "l", "x") print(s)   --> axx xii
s = string.gsub("Lua isgreat", "perl", "tcl") print(s)   --> Lua is great 

第四个参数是可选的,用来限制替换的范围:

s =string.gsub("all lii", "l", "x", 1) print(s)   --> axl lii
s = string.gsub("all lii", "l", "x", 2) print(s)   --> axx lii

string.gsub  的第二个返回值表示他进行替换操作的次数。例如,下面代码涌来计算一个字符串中空格出现的次数:

_, count= string.gsub(str, " ", " ")

(注意,_  只是一个哑元变量)

19.2模式

      你还可以在模式串中使用字符类。字符类指可以匹配一个特定字符集合内任何字符 的模式项。比如,字符类 %d匹配任 意数字。所以你可以使用模式 串 ‘%d%d/%d%d/%d%d%d%d‘搜索 dd/mm/yyyy 格式的日期:

s = "Deadline is30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date)))    --> 30/05/1999

下面的表列出了 Lua 支持的所有字符类:

.      任意字符
%a     字母
%c     控制字符
%d     数字
%l     小写字母
%p     标点字符
%s     空臼符
%u     大写字母
%w     字母和数字
%x     十六进制数字
%z     代表 0 的字符

上面字符类的大写形式表示小写所代表的集合的补集。例如,‘%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; wordword

‘%d+‘   匹配一个或多个数字C整数):

i, j =string.find("the number 1298is 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 库(上)的主要内容,如果未能解决你的问题,请参考以下文章

lua入门笔记7 字符串库

Python标准库笔记 — re模块

高速掌握Lua 5.3 —— 字符串库

Lua模式匹配

Lua模式匹配

Scala模式匹配和函数组合