快速掌握Lua 5.3 —— 字符串库 (3)

来源:互联网 发布:淘宝试用报告哪里看 编辑:程序博客网 时间:2024/06/07 14:17

Q:什么情况下”pattern”会匹配空串?

A:要小心的使用*-,因为它们可以匹配零次。

-- 如果你打算用"%a*"匹配单词,你会发现到处都是单词。print(string.find(";$%  **#$hello13", "%a*"))    --> 1    0print(string.find(";$%  **#$hello13", "%a*", 6))    --> 6    5-- 使用"%a+"才能正常的完成工作。print(string.find(";$%  **#$hello13", "%a+"))    --> 10    14

Q:如何使用Lua生成”pattern”?

A:使用Lua可以帮我们生成一些繁琐的”pattern”,

--[[ 查找一个文本中行字符大于70个的行,也就是匹配一个非换行符之前有70个字符的行。     重复匹配单个字符70次,后面跟着一个匹配单个字符0次或多次。]]pattern = string.rep("[^\n]", 70) .. "[^\n]*"-- 对于单词进行大小写无关的查找。function nocase (s)    -- 每找到一个子母,就将其转换为"[xX]"的形式。    s = string.gsub(s, "%a", function (c)            return string.format("[%s%s]", string.lower(c),                                           string.upper(c))        end)    return sendpattern = nocase("Hi there!")print(pattern)    -->  [hH][iI] [tT][hH][eE][rR][eE]!

Q:如何对目标串进行预处理?

A:预处理的意义在于排除特殊字符对匹配的影响。
第一个例子,将字符串中双引号内的字符串转换为大写,双引号之间可以包含转义的双引号\"

-- 将转义的双引号转换为"\ddd"的形式,其中"ddd"是双引号ASCII码的十进制表示。function code (s)    return (string.gsub(s, "\\(.)", function (x)        return string.format("\\%03d", string.byte(x))    end))end-- 将"\ddd"恢复为转义的双引号。function decode (s)    return (string.gsub(s, "\\(%d%d%d)", function (d)        return "\\" .. string.char(d)    end))ends = [[follows a typical string: "This is \"great\"!".]]--[[ 省去这步预处理,"great"将不会被转换为大写。     因为"This is \"是第一次匹配,"!"是第二次匹配。]]s = code(s)-- 使用"string.upper()"转换为大写。s = string.gsub(s, '(".-")', string.upper)s = decode(s)    -- 将预处理的部分复原。print(s)    --> follows a typical string: "THIS IS \"GREAT\"!".

第二个例子,我们来扩展“快速掌握Lua 5.3 —— 字符串库 (2)”中提到的”LaTeX”格式转为”XML”格式的例子。这一次的”LaTeX”格式中可以包含转义字符\,也就是说可以使用\\\{\},分别表示 \{}

--[[ 为了避免"\emph"和"\command"与"\{a\\b\}"混淆在一起,     我们首先应该将"\{"、"\\"和"\}"转换为特殊的编码,     然而与此同时不能将"\emph"和"\command"转换,     所以仅当"\"后面不是子母的时候才进行转换。     与上一个例子相同,转换为他们的"\ddd"的十进制形式。]]function code (s)    return (string.gsub(s, '\\(%A)', function (x)        return string.format("\\%03d", string.byte(x))    end))end-- 与上个例子相比,解码的时候不需要"\"了,所以可以直接调用"string.char()"。function decode (s)    return (string.gsub(s, '\\(%d%d%d)', string.char))ends = [[a \emph{command} is written as \command{text\{a\\b\}}.]]s = code(s)s = string.gsub(s, "\\(%a+){(.-)}", "<%1>%2</%1>")print(decode(s))--> a <emph>command</emph> is written as <command>text{a\b}</command>.

第三个例子是个稍微复杂的例子,我们来编解码”CSV”文件。
1、”CSV”文件的每一行表示一条记录,每一条记录由多个域组成,每一个域之间使用,分隔。
2、每一个域中的任何空格字符都是有效字符,不能被忽略。
3、如果域中包含,,那么整个域需要用""引起来,如果此时域中还包含",那么每一个"使用""代替。
4、不包含,的域也可以选择性的使用""引起来。但只要是使用""引起来的域,如果其中包含",那么每一个"都必须使用""代替。
根据以上规则,如若有表
t = {'a b', 'a,b', '', ' a,"b"c', 'hello "world"!'}
其中的元素转换为”CSV”文件格式应为,
a b,"a,b"," a,""b""c",,hello "world"!

print("Encode: ")function escapeCSV (s)    if string.find(s, ',') then    -- 如果域中有逗号,则整个域需要用双引号引起来。        -- 如果此时域中还有双引号,则每个双引号都应使用两个连续的双引号代替。        s = '"' .. string.gsub(s, '"', '""') .. '"'    end    return sendfunction toCSV (t)    local s = ""    for _,p in pairs(t) do        s = s .. "," .. escapeCSV(p)    -- 每个域使用","分隔。    end    return string.sub(s, 2)    -- remove first commaendt = {'a b', 'a,b', ' a,"b"c', '', 'hello "world"!'}s = toCSV(t)print(s)    --> a b,"a,b"," a,""b""c",,hello "world"!print()print("Decode: ")function fromCSV (s)    s = s .. ','    -- 在字符串末尾添加",",为了方便查找最后一个域。    local t = {}    -- table to collect fields    local fieldstart = 1    -- 域在字符串中的起始索引。    -- 循环的找出一条记录中的每一个域。    repeat        -- 如果域的起始位置是一个双引号,那么说明整个域被双引号引了起来。        if string.find(s, '^"', fieldstart) then            local a, c            local i  = fieldstart            repeat                --[[ 查找域尾的双引号。                     因为如果被双引号引起来的域中含有双引号字符的话,                     每个双引号都会使用连续的两个双引号代替。                     那么当找到连续的两个双引号时,"c"得到的是后面的那个双引号,                     "i"得到的是该双引号的索引位置,所以从"i+1"处继续寻找,                     即跳过了这两个连续的双引号继续寻找。                     而当找到一个双引号跟着一个非双引号字符时,此时找到了域尾的双引号。                     "c"得到的是个空字符串,"i"得到了双引号的索引位置。此时,完成查找。]]                a, i, c = string.find(s, '"("?)', i+1)            until c ~= '"'    -- quote not followed by quote?            if not i then error('unmatched "') end            -- 取出该域,舍弃域首尾的双引号。            local f = string.sub(s, fieldstart+1, i-1)            --[[ 因为已经舍弃了域首尾的双引号,                 所以域中所有连续的两个双引号恢复为单个双引号。                 将域的内容存入"table"中。]]            table.insert(t, (string.gsub(f, '""', '"')))            --[[ "i"是域尾双引号的索引位置,从此处寻找与下一个域之间的逗号,                 并更新下一个域的起始索引位置。                 因为在函数开始时,将最后一个域的末尾也加上了逗号,                 所以按照规范的写法,域尾双引号之后的字符就应该是逗号,                 所以此处实际可以写成"fieldstart = i + 2",                 而以下这种写法是为了防止不规范的写法,                 即域尾双引号与域间逗号之间有空格字符。]]            fieldstart = string.find(s, ',', i) + 1        else    -- 如果域的起始位置不是一个双引号,那么说明整个域未被双引号引起来。            -- 直接寻找下一个域间的逗号。            local nexti = string.find(s, ',', fieldstart)            -- 将域的内容存入"table"中。            table.insert(t, string.sub(s, fieldstart, nexti-1))            fieldstart = nexti + 1    -- 更新下一个域的起始索引位置。        end    until fieldstart > string.len(s)    return tendt = fromCSV(s)for i, s in ipairs(t) do print(i, s) end--[[ result:      1  a b     2  a,b     3   a,"b"c     4       5  hello "world"!]]print()--[[ 测试两个连续的双引号的不同作用。     每个域的内容依次为:     1、hello,空格,双引号,空格,hello。     2、空格,双引号,双引号。     3、无。]]t = fromCSV('"hello "" hello", "",""')for i, s in ipairs(t) do print(i, s) end--[[ result:      1  hello " hello     2   ""     3  ]]
0 0
原创粉丝点击