zoukankan      html  css  js  c++  java
  • Lua截取utf-8编码的中英文混合字符串

    参考博客:UTF8字符串在lua的截取和字数统计【转载】

    需求

    按字面个数来截取子字符串
    函数(字符串, 开始位置, 截取长度)
    
    utf8sub("你好1世界哈哈",2,5)    =    好1世界哈
    utf8sub("1你好1世界哈哈",2,5)    =    你好1世界
    utf8sub("你好世界1哈哈",1,5)    =    你好世界1
    utf8sub("12345678",3,5)    =    34567
    utf8sub("øpø你好pix",2,5)    =    pø你好p

    错误方法

    网上找了一些算法, 都不太正确; 要么就是乱码, 要么就是只考虑了4 byte 中文的情况, 不够全面

    1. string.sub(s,1,截取长度*4)

      网上很多直接使用"`""string.sub(s,1,截取长度*4)`"是肯定不对的, 因为如果中英文混合的字符串, 例如`你好1世界`的字符长度分别是`4,4,1,4,4`, 如果截取4个字, 4*4=4+4+1+4+3, 那`世界`的`界`字将会被取前3个byte, 就会出现乱码

    2. if byte>128 then index = index + 4

    问题关键

    1. utf8字符是变长字符

    2. 字符长度有规律

    如文字符编码中所列,utf-8是对unicode字符集的编码方案。因此其变长编码方式为:

    一字节:0*******

    两字节:110*****,10******

    三字节:1110****,10******,10******

    四字节:11110***,10******,10******,10******

    五字节:111110**,10******,10******,10******,10******

    六字节:1111110*,10******,10******,10******,10******,10******

    因此,拿到字节串后,想判断UTF8字符的byte长度,按照上文的规律,只需要获取该字符的首个Byte,根据其值就可以判断出该字符由几个Byte表示。

    其代码如下:

    local funciton charsize(ch)
        if not ch then return 0
        elseif ch >=252 then return 6
        elseif ch >= 248 and ch < 252 then return 5
        elseif ch >= 240 and ch < 248 then return 4
        elseif ch >= 224 and ch < 240 then return 3
        elseif ch >= 192 and ch < 224 then return 2
        elseif ch < 192 then return 1
        end
    end
    -- 计算utf8字符串字符数, 各种字符都按一个字符计算
    -- 例如utf8len("1你好") => 3
    function utf8len(str)
        local len = 0
        local aNum = 0 --字母个数
        local hNum = 0 --汉字个数
        local currentIndex = 1
        while currentIndex <= #str do
            local char = string.byte(str, currentIndex)
            local cs = charsize(char)
            currentIndex = currentIndex + cs
            len = len +1
            if cs == 1 then 
                aNum = aNum + 1
            elseif cs >= 2 then 
                hNum = hNum + 1
            end
        end
        return len, aNum, hNum
    end
    -- 截取utf8 字符串
    -- str:            要截取的字符串
    -- startChar:    开始字符下标,从1开始
    -- numChars:    要截取的字符长度
    function utf8sub(str, startChar, numChars)
        local startIndex = 1
        while startChar > 1 do
            local char = string.byte(str, startIndex)
            startIndex = startIndex + chsize(char)
            startChar = startChar - 1
        end
    
        local currentIndex = startIndex
    
        while numChars > 0 and currentIndex <= #str do
            local char = string.byte(str, currentIndex)
            currentIndex = currentIndex + chsize(char)
            numChars = numChars -1
        end
        return str:sub(startIndex, currentIndex - 1)
    end
    
    -- 自测
    function test()
        -- test utf8len
        assert(utf8len("你好1世界哈哈") == 7)
        assert(utf8len("你好世界1哈哈 ") == 8)
        assert(utf8len(" 你好世 界1哈哈") == 9)
        assert(utf8len("12345678") == 8)
        assert(utf8len("øpø你好pix") == 8)
    
        -- test utf8sub
        assert(utf8sub("你好1世界哈哈",2,5) == "好1世界哈")
        assert(utf8sub("1你好1世界哈哈",2,5) == "你好1世界")
        assert(utf8sub(" 你好1世界 哈哈",2,6) == "你好1世界 ")
        assert(utf8sub("你好世界1哈哈",1,5) == "你好世界1")
        assert(utf8sub("12345678",3,5) == "34567")
        assert(utf8sub("øpø你好pix",2,5) == "pø你好p")
    
        print("all test succ")
    end
    
    test()
  • 相关阅读:
    js--事件
    js之table操作
    2019年目标
    history.back返回后输入框值丢失问题
    C++ 工程师养成 每日一题4.5 (迭代器遍历)
    C++ 工程师养成 每日一题fourth (reverse的使用)
    C++ 工程师养成 每日一题third (子数列排序)
    C++工程师养成 每日一题(string使用)
    C++工程师养成 每日一题(vector使用)
    运算符优先级
  • 原文地址:https://www.cnblogs.com/slysky/p/6098981.html
Copyright © 2011-2022 走看看