lua字符串长度

 【关于unicode和utf-8】

# unicode字符集,包含了全世界的字符,然而它只是规定了字符的二进制编码,并没有规定二级制编码是如何存储的。utf-8就是unicode的一个实现方式,就是怎么存储和读取这个unicode二进制编码。

就像一张jpg图片,我们可以选择直接存放在硬盘上,也可以压缩成rar后保存,也可以压缩成7z后保存,UTF-8就类似这边的rar和7z。

 

【utf-8】

# utf-8是一个可变长的编码方式,有的是用1个字节存,有的采用2个,有的3个,最多可采用6个字节,既然是可变的,就要求字节中有个位数用来表示标记的,,有了标记就知道怎么读取字节了。

# 1个字节表示的编码,最高位是0,像这样0xxxxxxx。后面7位x来表示unicode编码,当然7位表示的编码有限。

# n个字节的编码,需要第一个byte的前n bit用1表示,n+1位用0,其余的字节前两位是10,如2个字节的 110xxxxx 10xxxxxx,3个字节的1110xxxx 10xxxxxx 10xxxxxx等的,除了符号位,剩下的x就用来表示unicode编码了,例如 "严"的unicode是4E25,二进制是01001110, 00100101,存储成utf-8的话需要3个字节(2个字节110xxxxx 10xxxxxx这种存不下)

 

【lua中获取字符串长度】

# lua中的字符串使用的是utf-8编码,如果直接使用string.len获取到的是字节(byte)长度,而不是字符(char)长度

---@param b byte 根据第1个字节判断utf-8字符存放字节大小
function string.utf8CharSize(b)
    if not b then return 0 end
    if b >= 0xfc then return 6 end -- 1111110x
    if b >= 0xf8 then return 5 end -- 111110xx
    if b >= 0xf0 then return 4 end -- 11110xxx
    if b >= 0xe0 then return 3 end -- 1110xxxx
    if b >= 0xc0 then return 2 end -- 110xxxxx
    return 1  -- 0xxxxxxx
end

---@param str string
---@return number
function string.utf8StringLen(str)
    if nil == str or "" == str then return 0 end
    assert("string" == type(str), "not string")
    
    local len = 0
    local i = 1
    while i <= #str do
        local b = string.byte(str, i)
        i = i + string.utf8CharSize(b)
        len = len + 1
    end
    return len
end

---@param str string
---@param index number|null 开始索引, 该索引的字符包含在内
---@param count number|null 裁剪数量
---@return string
function string.utf8StringSub(str, index, count)
    index = index or 1
    count = count or string.utf8StringLen(str)
    
    local byteCount = #str
    local i = 1
    --跳过起始的n个
    while index > 1 and i <= byteCount do
        local b = string.byte(str, i)
        i = i + string.utf8CharSize(b)
        index = index - 1
    end
    
    local j = i
    --子串结束位置
    while count > 0 and j <= byteCount do
        local b = string.byte(str, j)
        j = j + string.utf8CharSize(b)
        count = count - 1
    end
    
    return str:sub(i, j-1)
end

---@param str string
---@param index number 开始索引
---@param count number 数量
---@param dstTb any[]
---@param dstIndex number
---@return any[], number
function string.utf8CharArray(str, index, count, dstTb, dstIndex)
    if nil == index then index = 1 end
    if nil == count then count = string.utf8StringLen(str) end
    if nil == dstTb then dstTb = {} end
    
    local byteCount = #str
    local i = 1
    --跳过起始的n个
    while index > 1 and i <= byteCount do
        local b = string.byte(str, i)
        i = i + string.utf8CharSize(b)
        index = index - 1
    end
    
    local j = i
    local leftCount = count
        --子串结束位置
        if nil == dstIndex then
            while leftCount > 0 and j <= byteCount do
            local b = string.byte(str, j)
            local size = string.utf8CharSize(b)
            local utf8Char = str:sub(j, j+size-1)
            table.insert(dstTb, utf8Char)
            j = j + size
            leftCount = leftCount - 1
        end
    else
        while leftCount > 0 and j <= byteCount do
            local b = string.byte(str, j)
            local size = string.utf8CharSize(b)
            local utf8Char = str:sub(j, j+size-1)
            dstTb[dstIndex] = utf8Char
            j = j + size
            leftCount = leftCount - 1
            dstIndex = dstIndex + 1
        end
    end
    
    return dstTb, count
end

 

测试代码:

local function Test1()
    assert(6 == string.utf8StringLen("123abc"))
    assert(2 == string.utf8StringLen("中文"))
    assert(4 == string.utf8StringLen("中文1a"))
    assert(5 == string.utf8StringLen(".,;<>"))
    assert(5 == string.utf8StringLen("。,;《》"))
    assert(10 == string.utf8StringLen(".,;<>。,;《》"))
    
    assert("文1" == string.utf8StringSub("中文1a", 2, 2))
    assert("文啊" == string.utf8StringSub("中文啊呀", 2, 2))
    
    local tb = string.utf8CharArray("中文啊呀", 2, 2)
    assert("" == tb[1])
    assert("" == tb[2])
end

 

【参考】

Lua 的 # 获取长度 - 简书 (jianshu.com)

 

posted @ 2021-11-12 00:58  yanghui01  阅读(1528)  评论(0)    收藏  举报