1. 程式人生 > >Lua中含中文字串長度計算

Lua中含中文字串長度計算

轉自:http://m.blog.csdn.net/DanceWithCode1990/article/details/51154681

在專案中遇到了需要在Lua中計算含中文的字串的字串長度。

當時想當然的就用了string.len(“確定”) 發現輸出時竟然是6!後來才知道 ,由於檔案編碼格式是UTF-8,所以一箇中文是3個位元組!所以這也不難解釋為何“確定”的長度是6了。 至於UTF-8的前世今生,以及具體格式,網上有很多不錯的講解,以下是一位同行的形象講解。

“先從字元編碼講起。 
1、美國人首先對其英文字元進行了編碼,也就是最早的ascii碼,用一個位元組的低7位來表示英文的128個字元,高1位統一為0;

2、後來歐洲人發現尼瑪你這128位哪夠用,比如我高貴的法國人字母上面的還有註音符,這個怎麼區分,得,把高1位編進來吧,這樣歐洲普遍使用一個全位元組進行編碼,最多可表示256位。歐美人就是喜歡直來直去,字元少,編碼用得位數少;

3、但是即使位數少,不同國家地區用不同的字元編碼,雖然0–127表示的符號是一樣的,但是128–255這一段的解釋完全亂套了,即使2進位制完全一樣,表示的字元完全不一樣,比如135在法語,希伯來語,俄語編碼中完全是不同的符號;

4、更麻煩的是,尼瑪這電腦高科技傳到中國後,中國人發現我們有10萬多個漢字,你們歐美這256字塞牙縫都不夠。於是就發明了GB2312這些漢字編碼,典型的用2個位元組來表示絕大部分的常用漢字,最多可以表示65536個漢字字元,這樣就不難理解有些漢字你在新華字典裡查得到,但是電腦上如果不處理一下你是顯示不出來的了吧。

5、這下各用各的字符集編碼,這世界咋統一?俄國人發封email給中國人,兩邊字符集編碼不同,尼瑪顯示都是亂碼啊。為了統一,於是就發明了unicode,將世界上所有的符號都納入其中,每一個符號都給予一個獨一無二的編碼,現在unicode可以容納100多萬個符號,每個符號的編碼都不一樣,這下可統一了,所有語言都可以互通,一個網頁頁面裡可以同時顯示各國文字。

6、然而,unicode雖然統一了全世界字元的二進位制編碼,但沒有規定如何儲存啊,親。x86和amd體系結構的電腦小端序和大端序都分不清,別提計算機如何識別到底是unicode還是acsii了。如果Unicode統一規定,每個符號用三個或四個位元組表示,那麼每個英文字母前都必然有二到三個位元組是0,文字檔案的大小會因此大出二三倍,這對於儲存來說是極大的浪費。這樣導致一個後果:出現了Unicode的多種儲存方式。

7、網際網路的興起,網頁上要顯示各種字元,必須統一啊,親。utf-8就是Unicode最重要的實現方式之一。另外還有utf-16、utf-32等。UTF-8不是固定字長編碼的,而是一種變長的編碼方式。它可以使用1~4個位元組表示一個符號,根據不同的符號而變化位元組長度。這是種比較巧妙的設計,如果一個位元組的第一位是0,則這個位元組單獨就是一個字元;如果第一位是1,則連續有多少個1,就表示當前字元佔用多少個位元組。

8、注意unicode的字元編碼和utf-8的儲存編碼表示是不同的,例如”嚴”字的Unicode碼是4E25,UTF-8編碼是E4B8A5,這個7裡面解釋了的,UTF-8編碼不僅考慮了編碼,還考慮了儲存,E4B8A5是在儲存識別編碼的基礎上塞進了4E25。

9、UTF-8 使用一至四個位元組為每個字元編碼。128 個 ASCII 字元(Unicode 範圍由 U+0000 至 U+007F)只需一個位元組,帶有變音符號的拉丁文、希臘文、西裡爾字母、亞美尼亞語、希伯來文、阿拉伯文、敘利亞文及馬爾地夫語(Unicode 範圍由 U+0080 至 U+07FF)需要二個位元組,其他基本多文種平面(BMP)中的字元(CJK屬於此類-Qieqie注)使用三個位元組,其他 Unicode 輔助平面的字元使用四位元組編碼。

10、最後,要回答你的問題,常規來看,中文漢字在utf-8中到底佔幾個位元組,一般是3個位元組,最常見的編碼方式是1110xxxx 10xxxxxx 10xxxxxx”

這是他文章的具體地址:http://zhidao.baidu.com/link?url=ksgkUMF2YY_G8-egH_YdAQc2ialbbn__LwuTJMDgl-WkRJNXVkLkc2uYH2zSiUGjJw5aLHdXnTEUfexExPJqlydoCRc2i6NSOaoqdPNK1Au

由此可見,這個字元是否問中文是可以判斷的,因為如果是漢字 那麼這個字元的第一個位元組的高三位(即1110xxxx中的111)一定是111,第四位是0,所以這個位元組換算成數字的話最小值是224(11100000)最大值是238(11101111),所以如果我們讀到一個位元組,他的數值介於224與238之間,那麼我們就可以判定,這個位元組以及其後的兩個位元組,共三個位元組組成一個漢字。 
這樣我們就可以以此為基礎寫一個計算字元長度的函式:

function string.widthSingle(inputstr)
    -- 計算字串寬度
    -- 可以計算出字元寬度,用於顯示使用
   local lenInByte = #inputstr
   local width = 0
   local i = 1
   while (i<=lenInByte) 
    do
        local curByte = string.byte(inputstr, i)
        local byteCount = 1;
        if curByte>0 and curByte<=127 then
            byteCount = 1                                           --1位元組字元
        elseif curByte>=192 and curByte<223 then
            byteCount = 2                                           --雙位元組字元
        elseif curByte>=224 and curByte<239 then
            byteCount = 3                                           --漢字
        elseif curByte>=240 and curByte<=247 then
            byteCount = 4                                           --4位元組字元
        end

        local char = string.sub(inputstr, i, i+byteCount-1)
        print(char)                                                         

        i = i + byteCount                                 -- 重置下一位元組的索引
        width = width + 1                                 -- 字元的個數(長度)
    end
    return width
end

在學習時,我搜到了別的同學提供的方法 ,但這個方法似乎有錯誤: 
以下摘自:http://www.cocoachina.com/bbs/read.php?tid=221995

localstr = "Jimmy: 你好,世界!"
localfontSize = 20
locallenInByte = #str
localwidth = 0

fori=1,lenInByte do
    localcurByte = string.byte(str, i)
    localbyteCount = 1;
    ifcurByte>0 andcurByte<=127 then
        byteCount = 1
    elseifcurByte>=192 andcurByte<223 then
        byteCount = 2
    elseifcurByte>=224 andcurByte<239 then
        byteCount = 3
    elseifcurByte>=240 andcurByte<=247 then
        byteCount = 4
    end

    localchar = string.sub(str, i, i+byteCount-1)
    i = i + byteCount -1    
    ifbyteCount == 1 then
        width = width + fontSize * 0.5
    else
        width = width + fontSize
        print(char)
    end
end

print("總寬度: "..width)

這裡面有幾個問題 
1.lua中for迴圈中的i每次都是自增的文中的i = i + byteCount -1 並沒有改變i的值 
2.下一位元組的索引錯誤了i 
= i + byteCount -1 應該為i = i + byteCount