参考链接:
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库去处理是不行的。
1 --UTF-8编码,一个中文占3个字节 2 local a1 = "你好啊a" 3 4 print(string.byte(a1,1,4))--第1到第4个字节 5 print(string.len(a1))--字节总数 6 local startIndex, endIndex = string.find(a1, "你好") 7 print(startIndex .. " " .. endIndex)--第1到第6个字节 8 9 print("----------------------------------------------------") 10 local test = "泰" 11 local test2 = "法?" 12 13 print(string.len(test)) 14 print(string.byte(test,1,10)) 15 print(string.byte(test2,1,10)) 16 17 --string.gsub的第二个参数为正则表达式,?表示匹配0个至1个 18 --字节230179176中的230179被替换成989898 19 local str = string.gsub(test, test2, function() 20 print("gsub success!") 21 return "bbb" 22 end) 23 print(str) 24 print(string.byte(str,1,10)) 25 print(string.byte("b",1,10))
输出如下:
三.中文处理
先来测试一下中文是怎样匹配的:
1 --为了方便输出中文,这里使用ANSI编码 2 --在ANSI编码中,1个中文占2个字节 3 local test = "泰ab" 4 local result 5 6 print(string.byte(test,1,10))--泰:204169 a:97 b:98 7 print(type(string.byte(test,1,10)))--数字 8 9 --string.gsub 逐字节匹配 10 print("1.") 11 result = string.gsub(test, "[204169]", "c") 12 print(result)--[204169]:2,0,4,1,6,9的集合,因此匹配失败 13 14 print("2.") 15 result = string.gsub(test, "[\204169]", "c") 16 print(result) 17 print(string.byte(result,1,10))--第1个字节204匹配成功 18 print(string.byte("゛",1,10))--c:99 ゛:16997 b:98 19 20 print("3.") 21 result = string.gsub(test, "[\204\169]", "c") 22 print(result)--匹配成功2次 23 24 print("4.") 25 result = string.gsub(test, "[\204][\169]", "c") 26 print(result)--匹配成功1次,将原字符串中的中文替换了
输出如下:
UTF8的编码规则:
1.字符的第一个字节范围:(0-127)、(194-244)
2.字符的第二个字节及以后范围(针对多字节编码,如汉字):(128-191)
3.(192,193和245-255)不会出现在UTF8编码中
根据以上规则就可以得出处理中文的方法了:
1 --获取字符数 2 function GetWordCount(str) 3 local _,count = string.gsub(str, "[^\128-\193]", "") 4 return count 5 end 6 7 --将字符串转为table 8 function GetWordTable(str) 9 local temp = {} 10 for uchar in string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") do 11 temp[#temp+1] = uchar 12 end 13 return temp 14 end 15 16 --utf8 17 local test = "泰ab好了." 18 19 print(GetWordCount(test)) 20 local testT = GetWordTable(test) --%z:匹配0 *:表示0个至任意多个 21 for i=1,#testT do 22 print(testT[i]) 23 end
四.敏感字处理
敏感字的处理主要体现在取名、聊天上,如果字符串中含有敏感字,则需要将其替换成“*”。一开始我使用的string.gsub方法,但是发现敏感字中有不少是带有特殊符号,从而使整个字符串变成了一个正则表达式了,发生了正则匹配的错误,而正确的做法应该是直接跟敏感字进行对比。后来采用的是string.find方法,因为它可以关闭正则匹配。
1 local sensitiveWordConfig = {"法?"}; 2 3 function GetWordCount(str) 4 local _, count = string.gsub(str, "[^\128-\193]", "") 5 return count; 6 end 7 8 --内部接口:将字符串中的敏感字替换成*(替换一个) 9 function ReplaceSensitiveWord(originStr, sensitiveWord) 10 local resultStr = originStr; 11 --1:从索引1开始搜索 true:关闭模式匹配 12 local startIndex, endIndex = string.find(originStr, sensitiveWord, 1, true); 13 if (startIndex and endIndex) then 14 local strLen = string.len(originStr); 15 local maskWordCount = GetWordCount(sensitiveWord); 16 local maskWord = ""; 17 for i=1,maskWordCount do 18 maskWord = maskWord .. "*"; 19 end 20 -- print(string.format("startIndex: %d endIndex: %d", startIndex, endIndex)); 21 -- print(string.format("strLen: %s maskWord: %s", strLen, maskWord)); 22 23 if (startIndex == 1) then 24 resultStr = maskWord .. string.sub(originStr, endIndex + 1, -1); 25 elseif (endIndex == strLen) then 26 resultStr = string.sub(originStr, 1, startIndex - 1) .. maskWord; 27 else 28 local str = string.sub(originStr, 1,startIndex - 1); 29 local str2 = string.sub(originStr, endIndex + 1, -1); 30 resultStr = str .. maskWord .. str2; 31 end 32 end 33 return resultStr; 34 end 35 36 --内部接口:将字符串中的敏感字替换成*(替换所有) 37 function ReplaceSensitiveWordAll(originStr, sensitiveWord) 38 local str = originStr; 39 local str2 = ReplaceSensitiveWord(originStr, sensitiveWord); 40 while (str ~= str2) do 41 str = str2; 42 str2 = ReplaceSensitiveWord(str2, sensitiveWord); 43 end 44 return str2; 45 end 46 47 --内部接口:是否有该敏感字 48 function HasSensitiveWord(originStr, sensitiveWord) 49 local startIndex, endIndex = string.find(originStr, sensitiveWord, 1, true); 50 if (startIndex and endIndex) then 51 -- print("敏感字:" .. sensitiveWord); 52 return true; 53 else 54 return false; 55 end 56 end 57 58 --外部接口:敏感字替换 59 function ReplaceMaskWord(content) 60 for k,v in pairs(sensitiveWordConfig) do 61 content = ReplaceSensitiveWordAll(content, v); 62 end 63 return content; 64 end 65 66 --外部接口:是否有敏感字 67 function HasMaskWord(content) 68 for k,v in pairs(sensitiveWordConfig) do 69 if (HasSensitiveWord(content, v)) then 70 return true; 71 end 72 end 73 return false; 74 end 75 76 print(ReplaceSensitiveWord("法?123法?", "法?")); 77 print(ReplaceSensitiveWordAll("法?123法?", "法?")); 78 print(HasSensitiveWord("12中法?3文", "法?")); 79 print(ReplaceMaskWord("1法?法?2")); 80 print(HasMaskWord("1法?法?2"));