Lua 字符串处理

参考链接:

https://baike.baidu.com/item/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81/8446880?fr=aladdin#7

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

 http://blog.csdn.net/r0ck_y0u/article/details/51883955

一.字符编码

字符编码的发展史:ASCII->Unicode->UTF-8

1.ASCII:ASCII码可以表示所有的英语字符(字母、数字、标点符号等)。ASCII码是7位编码(0-127),但由于计算机基本处理单位为字节(1字节=8位),所以一个ASCII字符占一个字节。

2.Unicode:因为一个ASCII字符只能表示256个字符,显然是存在着局限的(如不能用来表示中文)。而且不同的语言有不同的字符,为了让世界上所有的字符都有一个唯一的编码值(如果一个编码值对应多个字符,就会出现歧义),就出现了Unicode码。Unicode码可以容纳100多万个符号,每个符号的编码都不一样。但是Unicode码的缺点是效率不高,比如UCS-4(Unicode的标准之一)规定用4个字节存储一个符号,那么每个英文字母前都必然有三个字节是0,原本只需1个字节现在却用了4个字节,这对存储和传输来说都很耗资源。

3.UTF-8:为了提高Unicode的编码效率,于是就出现了UTF-8编码。UTF-8可以根据不同的符号自动选择编码的长短。在UTF-8中,一个英文占1个字节,一个中文占3个字节。

二.string库

相关api:http://cloudwu.github.io/lua53doc/manual.html#pdf-string.sub

在lua中,string库都是针对单字节字符编码的。在UTF-8中,因为英语字符都是单字节字符,所以使用string库处理英语字符是没有问题的;但是中文字符是多字节字符,如果使用string库去处理是不行的。

--UTF-8编码,一个中文占3个字节
local a1 = "你好啊a"

print(string.byte(a1,1,4))--第1到第4个字节
print(string.len(a1))--字节总数
local startIndex, endIndex = string.find(a1, "你好")
print(startIndex .. " " .. endIndex)--第1到第6个字节

print("----------------------------------------------------")
local test = "泰"
local test2 = "法?"

print(string.len(test))
print(string.byte(test,1,10))
print(string.byte(test2,1,10))

--string.gsub的第二个参数为正则表达式,?表示匹配0个至1个
--字节230179176中的230179被替换成989898
local str = string.gsub(test, test2, function()
    print("gsub success!")
    return "bbb"
end)
print(str)
print(string.byte(str,1,10))
print(string.byte("b",1,10))

输出如下:

三.中文处理

 先来测试一下中文是怎样匹配的:

--为了方便输出中文,这里使用ANSI编码
--在ANSI编码中,1个中文占2个字节
local test = "泰ab"
local result

print(string.byte(test,1,10))--泰:204169 a:97 b:98
print(type(string.byte(test,1,10)))--数字

--string.gsub 逐字节匹配
print("1.")
result = string.gsub(test, "[204169]", "c")
print(result)--[204169]:2,0,4,1,6,9的集合,因此匹配失败

print("2.")
result = string.gsub(test, "[\204169]", "c")
print(result)
print(string.byte(result,1,10))--第1个字节204匹配成功
print(string.byte("゛",1,10))--c:99 ゛:16997 b:98

print("3.")
result = string.gsub(test, "[\204\169]", "c")
print(result)--匹配成功2次

print("4.")
result = string.gsub(test, "[\204][\169]", "c")
print(result)--匹配成功1次,将原字符串中的中文替换了

输出如下:

UTF8的编码规则:

1.字符的第一个字节范围:(0-127)、(194-244)

2.字符的第二个字节及以后范围(针对多字节编码,如汉字):(128-191)

3.(192,193和245-255)不会出现在UTF8编码中 

根据以上规则就可以得出处理中文的方法了:

--获取字符数
function GetWordCount(str)
    local _,count = string.gsub(str, "[^\128-\193]", "")
    return count
end

--将字符串转为table
function GetWordTable(str)
    local temp = {}
    for uchar in string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") do
        temp[#temp+1] = uchar
    end
    return temp
end

--utf8
local test = "泰ab好了."

print(GetWordCount(test))
local testT = GetWordTable(test) --%z:匹配0 *:表示0个至任意多个
for i=1,#testT do
    print(testT[i])
end

四.敏感字处理

敏感字的处理主要体现在取名、聊天上,如果字符串中含有敏感字,则需要将其替换成“*”。一开始我使用的string.gsub方法,但是发现敏感字中有不少是带有特殊符号,从而使整个字符串变成了一个正则表达式了,发生了正则匹配的错误,而正确的做法应该是直接跟敏感字进行对比。后来采用的是string.find方法,因为它可以关闭正则匹配。

local sensitiveWordConfig = {"法?"};

function GetWordCount(str)
    local _, count = string.gsub(str, "[^\128-\193]", "")
    return count;
end

--内部接口:将字符串中的敏感字替换成*(替换一个)
function ReplaceSensitiveWord(originStr, sensitiveWord)
    local resultStr = originStr;
    --1:从索引1开始搜索 true:关闭模式匹配
    local startIndex, endIndex = string.find(originStr, sensitiveWord, 1, true);
    if (startIndex and endIndex) then
        local strLen = string.len(originStr);
        local maskWordCount = GetWordCount(sensitiveWord);
        local maskWord = "";
        for i=1,maskWordCount do
            maskWord = maskWord .. "*";
        end
        -- print(string.format("startIndex: %d endIndex: %d", startIndex, endIndex));
        -- print(string.format("strLen: %s maskWord: %s", strLen, maskWord));

        if (startIndex == 1) then
            resultStr = maskWord .. string.sub(originStr, endIndex + 1, -1);
        elseif (endIndex == strLen) then
            resultStr = string.sub(originStr, 1, startIndex - 1) .. maskWord;
        else
            local str = string.sub(originStr, 1,startIndex - 1);
            local str2 = string.sub(originStr, endIndex + 1, -1);
            resultStr = str .. maskWord .. str2;
        end
    end
    return resultStr;
end

--内部接口:将字符串中的敏感字替换成*(替换所有)
function ReplaceSensitiveWordAll(originStr, sensitiveWord)
    local str = originStr;
    local str2 = ReplaceSensitiveWord(originStr, sensitiveWord);
    while (str ~= str2) do
        str = str2;
        str2 = ReplaceSensitiveWord(str2, sensitiveWord);
    end
    return str2;
end

--内部接口:是否有该敏感字
function HasSensitiveWord(originStr, sensitiveWord)
    local startIndex, endIndex = string.find(originStr, sensitiveWord, 1, true);
    if (startIndex and endIndex) then
        -- print("敏感字:" .. sensitiveWord);
        return true;
    else
        return false;
    end
end

--外部接口:敏感字替换
function ReplaceMaskWord(content)
    for k,v in pairs(sensitiveWordConfig) do
        content = ReplaceSensitiveWordAll(content, v);
    end
    return content;
end

--外部接口:是否有敏感字
function HasMaskWord(content)
    for k,v in pairs(sensitiveWordConfig) do
        if (HasSensitiveWord(content, v)) then
            return true;
        end
    end
    return false;
end

print(ReplaceSensitiveWord("法?123法?", "法?"));
print(ReplaceSensitiveWordAll("法?123法?", "法?"));
print(HasSensitiveWord("12中法?3文", "法?"));
print(ReplaceMaskWord("1法?法?2"));
print(HasMaskWord("1法?法?2"));

string.gsub()

  • 原型:string.gsub (s, pattern, repl [,m])
  • 解释:这个函数会返回一个替换后的副本,原串中所有的符合参数pattern的子串都将被参数repl所指定的字符串所替换,如果指定了参数m,那么只替换查找过程的前m个匹配的子串,参数repl可以是一个字符串、表、或者是函数,并且函数可以将匹配的次数作为函数的第二个参数返回,接下来看看参数repl的含义:
  • 如果参数repl是一个常规字符串,成功匹配的子串会被repl直接替换,如果参数repl中包含转移字符%,那么可以采用%n的形式替换,当%n中的n取值1-9时,表示一次匹配中的第n个子串,当其中的n为0时,表示这次匹配的整个子串,%%表示一个单独的%
  • 如果参数repl是一个表,那么每次匹配中的第一个子串将会作为整个表的键,取table[匹配子串]来替换所匹配出来的子串,当匹配不成功时,函数会使用整个字符串来作为table的键值。
  • 如果参数repl是一个函数,那么每一次匹配的子串都将作为整个函数的参数,取function(匹配子串)来替换所匹配出来的子串,当匹配不成功时,函数会使用整个字符串来作为函数的参数。如果函数的返回值是一个数字或者是字符串,那么会直接拿来替换,如果它返回false或者nil,替换动作将不会发生,如果返回其他的值将会报错。
元字符描述表达式实例完整匹配的字串
字符
普通字符除去%.[]()^$*+-?的字符,匹配字符本身KanaKana
.匹配任意字符Ka.aKana
%转义字符,改变后一个字符的原有意思。当后面的接的是特殊字符时,将还原特殊字符的原意。%和一些特定的字母组合构成了lua的预定义字符集。%和数字1~9组合表示之前捕获的分组K%wna
%%na%%
(a)n%1
Kana
%na%
ana
[...]字符集(字符类)。匹配一个包含于集合内的字符。[...]中的特殊字符将还原其原意,但有下面几种特殊情况
1. %],%-,%^作为整体表示字符']','-','^'
2. 预定义字符集作为一个整体表示对应字符集
3. 当]位于序列的第一个字符时只表示字符']'
4. 形如[^...],[...-...]有特定的其他含义
[a%]na
[%a]na
[%%a]na
[]]na
[%]]na
[a-]na
%na
wna
wna
]na
]na
-na
[...-...]-表示ascii码在它前一个字符到它后一个字符之间的所有字符[a-z]ana
[^...]不在...中的字符集合。[^0-9]na
[^^0-9]na
Kna
Kna
重复(数量词)
*表示前一个字符出现0次或多次[0-9]*
[a-z]*9*
2009
na
+表示前一个字符出现1次或1次以上n+[0-9]+n2009
?表示前一个字符出现0次或1次n?[0-9]+2009
预定义字符集
%s空白符[ \r\n\t\v\f]an[%s]?9an 9
%p标点符号an[%p]9an.9
%c控制字符
%w字母数字[a-zA-Z0-9][%w]+Kana9
%a字母[a-zA-Z][%a]*Kana
%l小写字母[a-z]-
%u大写字母[A-Z]-
%d数字[0-9]-
%x16进制数[0-9a-fA-F]-
%zascii码是0的字符-
分组
(...)表达式中用小括号包围的子字符串为一个分组,分组从左到右(以左括号的位置),组序号从1开始递增。ab(%d+)
(%d+)%1
ab233
123123
边界匹配(属于零宽断言)
^匹配字符串开头^(%a)%w*abc123
$匹配字符串结尾%w*(%d)$abc123
%b
%bxy平衡匹配(匹配xy对)。这里的x,y可以是任何字符,即使是特殊字符也是原来的含义,匹配到的子串以x开始,以y结束,并且如果从x开始,每遇到x,计算+1,遇到y计数-1,则结束的y是第一个y使得计数等于0。就是匹配成对的符号,常见的如%b()匹配成对的括号

需要用到的lua底层函数:

string.sub():Lua string.sub() - 简书

string.gsub():Lua string.gsub() - 简书

string.find():Lua string.find() - 简书

本文转载自:[Lua]string与中文 - 艰苦奋斗中 - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值