博客專欄

        EEPW首頁 > 博客 > 技術實踐|數(shù)據(jù)遷移中GBK轉(zhuǎn)UTF8字符集問題分析

        技術實踐|數(shù)據(jù)遷移中GBK轉(zhuǎn)UTF8字符集問題分析

        發(fā)布人:中電金信人 時間:2024-03-04 來源:工程師 發(fā)布文章

        導語:在國產(chǎn)化創(chuàng)新的大背景下,數(shù)據(jù)庫遷移項目逐漸增多,在數(shù)據(jù)庫遷移過程中,源數(shù)據(jù)庫和目標數(shù)據(jù)庫字符集有時會不同,這時如何進行字符集轉(zhuǎn)換則成為了一個重要的問題,同時在轉(zhuǎn)換過程中還需要確保數(shù)據(jù)的完整性和一致性。


        字符集轉(zhuǎn)換算法是一個復雜的領域,因此各個操作系統(tǒng)和庫實現(xiàn)可能會有所不同。此外,一些特定的字符集轉(zhuǎn)換還可能會涉及更復雜的操作。例如字符替換、丟棄或使用替代字符表示無法轉(zhuǎn)換的字符等。因此,實際的字符集轉(zhuǎn)換結(jié)果可能會因使用的庫、操作系統(tǒng)版本以及具體的轉(zhuǎn)換需求而有所差異。



        1. 字符集介紹


        ■ASCII:

        ASCII(American Standard Code for Information Interchange)是一個基于拉丁字母的字符集編碼方案,使用7位(8位的擴展ASCII)來表示字符。


        ASCII字符集包含了基本的拉丁字母、數(shù)字、標點符號和一些特殊控制字符,共計128個字符。


        ASCII是一個較為簡單和有限的字符集,主要適用于英語及其他使用基本拉丁字母的語言。


        ■ Latin-1:

        Latin-1是一種拉丁字符集編碼方案,使用8位(一個字節(jié))來表示每個字符。


        Latin-1(ISO 8859-1)覆蓋了ASCII字符集的范圍,并擴展了一些額外的特殊字符和符號,包括重音符號、貨幣符號、擴展的拉丁字母等。


        Latin-1適用于多種西歐語言,如英語、法語、德語、西班牙語等,能夠表示這些語言中常見的字符需求。


        ■ GBK:

        GBK是一種中文字符集編碼,主要用于表示中文字符和標點符號。它是GB2312(國標2312)的擴展版本,支持更多的漢字字符。


        GBK使用雙字節(jié)編碼,每個字符占用兩個字節(jié)。其中,ASCII字符的編碼與ASCII字符集兼容,非ASCII字符則使用兩個字節(jié)來表示。


        GBK能夠表示包括繁體中文、簡體中文在內(nèi)的大部分中文字符。


        ■ UTF-8:

        UTF-8是一種通用的字符集編碼,支持全球范圍內(nèi)的幾乎所有字符,包括各種語言的文字、符號和表情符號。


        UTF-8使用變長編碼,根據(jù)字符的Unicode值,使用1到4個字節(jié)來表示字符。其中,ASCII字符使用一個字節(jié)表示,非ASCII字符使用多個字節(jié)表示。


        UTF-8兼容ASCII字符集,可以表示所有ASCII字符,因此它是廣泛使用的字符集編碼方案。



        2. 數(shù)據(jù)遷移背景介紹


        早期的數(shù)據(jù)倉庫字符集一般都是GBK,而現(xiàn)在的數(shù)據(jù)倉庫都使用UTF8字符集,所以字符集轉(zhuǎn)換是遷移過程中最關鍵的一個步驟。正常情況下如果源數(shù)據(jù)庫沒有亂碼,那么字符集轉(zhuǎn)換不會出現(xiàn)問題,GBK可以正常轉(zhuǎn)換為UTF8。但如果源數(shù)據(jù)庫有亂碼存在,那么在字符集轉(zhuǎn)換過程中就會出現(xiàn)很多不確定的問題,而且不同的字符集轉(zhuǎn)換方式不同,結(jié)果也不同。



        3. 字符集轉(zhuǎn)換方法介紹


        目前字符集轉(zhuǎn)換采用兩種方式:

         Linux系統(tǒng)的iconv

         編寫程序?qū)崿F(xiàn)字符集轉(zhuǎn)換,推薦使用Golang、Python、C,考慮到項目實施的可操作性和技術通用性,一般可以采用Python語言,且可以通過多線程提高轉(zhuǎn)碼效率。


        ● iconv

        iconv是一個在Linux和其他類Unix操作系統(tǒng)上廣泛使用的命令行工具。它用于進行字符編碼之間的轉(zhuǎn)換。iconv的名稱是“character set conversion”(字符集轉(zhuǎn)換)的縮寫。


        在Linux系統(tǒng)中,iconv命令使用的字符集轉(zhuǎn)換算法主要依賴于GNU C庫(GNU C Library,簡稱為glibc)提供的轉(zhuǎn)換功能。glibc是Linux系統(tǒng)的標準C庫,為許多基本操作提供了支持,包括字符集轉(zhuǎn)換。


        glibc中的字符集轉(zhuǎn)換算法主要基于Unicode標準:Unicode是一種字符編碼標準,它為世界上幾乎所有的字符提供了唯一的編碼值。glibc使用Unicode標準作為內(nèi)部字符表示,以實現(xiàn)不同字符集之間的轉(zhuǎn)換。


        ● Python的codecs模塊

        codecs是Python標準庫中的一個模塊,用于字符編碼和解碼操作。它提供了一組函數(shù)和類,用于在不同的字符編碼之間進行轉(zhuǎn)換。在處理文本數(shù)據(jù)時,經(jīng)常需要將文本從一種編碼格式轉(zhuǎn)換為另一種編碼格式。這可能涉及到將文本從Unicode轉(zhuǎn)換為其他編碼(如UTF-8、ASCII等),或者將文本從其他編碼轉(zhuǎn)換為Unicode。codecs模塊提供了一種簡單而一致的方式來執(zhí)行這些編碼和解碼操作。


        以下是codecs模塊的一些主要特性和功能:


        編碼和解碼函數(shù):codecs模塊提供了一組函數(shù),如codecs.encode()和codecs.decode(),用于執(zhí)行字符編碼和解碼操作。這些函數(shù)接受輸入文本和目標編碼格式作為參數(shù),并返回編碼或解碼后的文本。


        多種編碼支持:codecs模塊支持許多常見的字符編碼格式,包括ASCII、UTF-8、UTF-16、UTF-32等。它還提供了對其他編碼格式的支持,如Base64、Quoted-Printable、ROT13等。


        錯誤處理:在進行字符編碼和解碼時,可能會出現(xiàn)無法處理的字符或編碼錯誤。codecs 模塊允許指定不同的錯誤處理策略,以處理這些錯誤情況。例如,可以選擇忽略無法處理的字符,替換它們或引發(fā)異常。


        使用codecs模塊,可以便捷地進行不同編碼之間的轉(zhuǎn)換,處理文本數(shù)據(jù)的編碼問題,并確保數(shù)據(jù)在不同環(huán)境中正確地傳輸和解釋。



        4. 項目實施中字符集轉(zhuǎn)換介紹


        以TERADATA(TD)數(shù)據(jù)庫遷移到高斯數(shù)據(jù)庫為例,一般TD數(shù)據(jù)庫默認是使用latin1的字符集,而應用一般使用中文GBK字符集在TD數(shù)據(jù)庫中存儲數(shù)據(jù),所以當從TD數(shù)據(jù)庫遷移到其他數(shù)據(jù)庫時,應該以GBK字符集作為源數(shù)據(jù)庫字符集。


        數(shù)據(jù)遷移主要流程如下:

        從TD數(shù)據(jù)庫中導出數(shù)據(jù)并以GBK字符集落地為數(shù)據(jù)文件。

        將GBK數(shù)據(jù)文件轉(zhuǎn)換為UTF8文件。

        將UTF8數(shù)據(jù)文件導入到高斯數(shù)據(jù)庫(高斯數(shù)據(jù)庫的外表加載也可以將GBK字符集轉(zhuǎn)換為UTF8字符集,在此不做討論)


        某證券公司的業(yè)務表部分示例數(shù)據(jù)如下,從TD數(shù)據(jù)庫中導出的數(shù)據(jù)是GBK字符集,數(shù)據(jù)中有3個字段,字段分隔符為:||,數(shù)據(jù)的第三個字段是中文。在遷移過程中中文字段可能會存在亂碼,所以在使用不同的字符集轉(zhuǎn)換方式后其轉(zhuǎn)換的結(jié)果也會有所不同。


        示例數(shù)據(jù)中第一行的第三個中文字段有亂碼,正確的數(shù)據(jù)如下:

        G000A||10000||廣東省廣州市天河區(qū)天河北路437號

        E000D||20000||上海市浦東新區(qū)來安路685號

        Q000D||20000||山東省青島市嶗山區(qū)仙霞嶺路17~21號


        第一行中文字段的GBK十六進制編碼如下:



        圖片



        數(shù)據(jù)中“州”字的GBK編碼:D6 DD,但是實際的數(shù)據(jù)中由于某種原因造成D6丟失,由于GBK是雙字節(jié)編碼,所以DD和后面的字節(jié)(CA)重新組成了另一個漢字:菔,而以此類推后面的漢字,每兩個字節(jié)組成一個漢字,但B7 34在GBK編碼中不能組成漢字,34在GBK編碼中是:4,也正是“437號”中的“4”。



        圖片



        當使用iconv轉(zhuǎn)換此帶有亂碼的GBK文件時,效果如下所示。


        iconv系統(tǒng)內(nèi)核版本、os版本、自身版本如下:

        [root@imo tmp]# uname -r
        3.10.0-514.el7.x86_64
        [root@imo tmp]# cat /etc/redhat-release
        Red Hat Enterprise Linux Server release 7.3 (Maipo)
        [root@imo tmp]# iconv -V
        iconv (GNU libc) 2.17


        轉(zhuǎn)換命令如下:

        [root@imo tmp]# iconv -f gbk -t utf8 -c  sec_acc_gbk.txt  -o sec_acc_utf8.txt


        所以經(jīng)過iconv轉(zhuǎn)換后,B7和34不能組成漢字,所以B7被丟棄,而實際的內(nèi)容如下:

        G000A||10000||廣東省廣菔刑旌憂旌穎甭437號

        E000D||20000||上海市浦東新區(qū)來安路685號

        Q000D||20000||山東省青島市嶗山區(qū)仙霞嶺路17~21號


        當python程序使用內(nèi)置庫codecs進行代碼轉(zhuǎn)換后,可以有2個參數(shù)選項errors='replace'和errors='ignore',‘replace’表示當出現(xiàn)亂碼后可以把亂碼替換成“?”,而'ignore'表示當出現(xiàn)亂碼后,會把亂碼丟棄(和iconv特性相同)。


        當使用codecs做代碼轉(zhuǎn)換時,使用'replace'參數(shù),部分代碼如下:

        codecs.open(fileGbkAPName, 'r', encoding='{0}'.format(gbkFileEncoding),errors='replace')


        轉(zhuǎn)換后的結(jié)果如下:

        G000A||10000||廣東省廣?菔刑旌憂?天河北路437號

        E000D||20000||上海市浦東新區(qū)來安路685號

        Q000D||20000||山東省青島市嶗山區(qū)仙霞嶺路17~21號


        當使用codecs做代碼轉(zhuǎn)換時,使用'ignore'參數(shù),部分代碼如下:

        codecs.open(fileGbkAPName, 'r', encoding='{0}'.format(gbkFileEncoding),errors='ignore')


        轉(zhuǎn)換后的結(jié)果如下:

        G000A||10000||廣東省廣菔刑旌憂天河北路437號

        E000D||20000||上海市浦東新區(qū)來安路685號

        Q000D||20000||山東省青島市嶗山區(qū)仙霞嶺路17~21號



        5. 總結(jié)


         iconv 2.17版本就是根據(jù)glibc庫進行字符集轉(zhuǎn)換,不能轉(zhuǎn)換的就丟棄,且當文件中有半個字節(jié)丟失后,后面轉(zhuǎn)換的中文字符很可能是不準確的。如在本示例中,遇到亂碼后,最終轉(zhuǎn)換的字符為:“菔刑旌憂旌穎甭437號”


         Python的內(nèi)置庫codecs對中文轉(zhuǎn)換時采用一種“轉(zhuǎn)換最多中文字符”的策略,所以codecs在本示例中,遇到亂碼后,最終轉(zhuǎn)換的字符為:“菔刑旌憂天河北路437號”。



        6. Python程序示例


        # -*- coding: utf-8 -*-
        import codecs
        import sys
        
        
        ## 定義常量
        fileGbkAPName="/DATA/GBK_FILES/sec_acc_gbk.txt"
        fileUtf8APName="/DATA/UTF8_FILES/sec_acc_utf8.txt"
        gbkFileEncoding='gbk'
        utf8FileEncoding='utf8'
        
        
        def main():
        
        
            try:  # open TD數(shù)據(jù)文件(使用codecs庫)
                gbkFileStream = codecs.open(fileGbkAPName, 'rb', encoding='{0}'.format(gbkFileEncoding),errors='replace')
                # gbkFileStream = codecs.open(fileGbkAPName, 'rb', encoding='{0}'.format(gbkFileEncoding),errors='ignore')
        
        
            except Exception as e :
        
        
                print("不能Open數(shù)據(jù)文件{0},報錯信息{1},程序異常退出!!".format(fileGbkAPName,e))
                sys.exit(-1)
        
        
            tmpGbkCont = gbkFileStream.readlines()
        
        
            # 轉(zhuǎn)換為utf8字符
            utf8FileStream= open(f'{fileUtf8APName}','w',encoding=f'{utf8FileEncoding}')
            for gbkLine in tmpGbkCont:
        
        
                utf8Line = gbkLine.encode('{0}'.format(utf8FileEncoding)).decode('{0}'.format(utf8FileEncoding)).split('\n')[0]
                print(utf8Line)
        
        
                # 寫入utf8文件
                utf8FileStream.write(utf8Line+'\n')
        
        
            gbkFileStream.close()
            utf8FileStream.close()
        
        
        if __name__ == '__main__':
            main()
        else:
            print("程序執(zhí)行非法調(diào)用,異常退出!!")
            sys.exit(-1)


        *博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權請聯(lián)系工作人員刪除。



        關鍵詞: 人工智能

        相關推薦

        技術專區(qū)

        關閉
        主站蜘蛛池模板: 区。| 孟州市| 自治县| 广汉市| 龙门县| 留坝县| 莎车县| 石台县| 南通市| 怀宁县| 资中县| 来宾市| 东山县| 科技| 怀来县| 和硕县| 台江县| 新邵县| 彰武县| 永平县| 运城市| 双流县| 满洲里市| 通城县| 彰武县| 南木林县| 饶河县| 新津县| 元谋县| 苏尼特右旗| 房产| 明水县| 双城市| 金塔县| 乌鲁木齐市| 文安县| 昌吉市| 三穗县| 济南市| 页游| 南澳县|