技术实践|数据迁移中GBK转UTF8字符集问题分析

导语:在国产化创新的大背景下,数据库迁移项目逐渐增多,在数据库迁移过程中,源数据库和目标数据库字符集有时会不同,这时如何进行字符集转换则成为了一个重要的问题,同时在转换过程中还需要确保数据的完整性和一致性。

字符集转换算法是一个复杂的领域,因此各个操作系统和库实现可能会有所不同。此外,一些特定的字符集转换还可能会涉及更复杂的操作。例如字符替换、丢弃或使用替代字符表示无法转换的字符等。因此,实际的字符集转换结果可能会因使用的库、操作系统版本以及具体的转换需求而有所差异。

1. 字符集介绍

■ASCII:

ASCII(American Standard Code for Information Interchange)是一个基于拉丁字母的字符集编码方案,使用7位(8位的扩展ASCII)来表示字符。

ASCII字符集包含了基本的拉丁字母、数字、标点符号和一些特殊控制字符,共计128个字符。

ASCII是一个较为简单和有限的字符集,主要适用于英语及其他使用基本拉丁字母的语言。

■ Latin-1:

Latin-1是一种拉丁字符集编码方案,使用8位(一个字节)来表示每个字符。

Latin-1(ISO 8859-1)覆盖了ASCII字符集的范围,并扩展了一些额外的特殊字符和符号,包括重音符号、货币符号、扩展的拉丁字母等。

Latin-1适用于多种西欧语言,如英语、法语、德语、西班牙语等,能够表示这些语言中常见的字符需求。

■ GBK:

GBK是一种中文字符集编码,主要用于表示中文字符和标点符号。它是GB2312(国标2312)的扩展版本,支持更多的汉字字符。

GBK使用双字节编码,每个字符占用两个字节。其中,ASCII字符的编码与ASCII字符集兼容,非ASCII字符则使用两个字节来表示。

GBK能够表示包括繁体中文、简体中文在内的大部分中文字符。

■ UTF-8:

UTF-8是一种通用的字符集编码,支持全球范围内的几乎所有字符,包括各种语言的文字、符号和表情符号。

UTF-8使用变长编码,根据字符的Unicode值,使用1到4个字节来表示字符。其中,ASCII字符使用一个字节表示,非ASCII字符使用多个字节表示。

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

2. 数据迁移背景介绍

早期的数据仓库字符集一般都是GBK,而现在的数据仓库都使用UTF8字符集,所以字符集转换是迁移过程中最关键的一个步骤。正常情况下如果源数据库没有乱码,那么字符集转换不会出现问题,GBK可以正常转换为UTF8。但如果源数据库有乱码存在,那么在字符集转换过程中就会出现很多不确定的问题,而且不同的字符集转换方式不同,结果也不同。

3. 字符集转换方法介绍

目前字符集转换采用两种方式:

■ Linux系统的iconv

■ 编写程序实现字符集转换,推荐使用Golang、Python、C,考虑到项目实施的可操作性和技术通用性,一般可以采用Python语言,且可以通过多线程提高转码效率。

● iconv

iconv是一个在Linux和其他类Unix操作系统上广泛使用的命令行工具。它用于进行字符编码之间的转换。iconv的名称是“character set conversion”(字符集转换)的缩写。

在Linux系统中,iconv命令使用的字符集转换算法主要依赖于GNU C库(GNU C Library,简称为glibc)提供的转换功能。glibc是Linux系统的标准C库,为许多基本操作提供了支持,包括字符集转换。

glibc中的字符集转换算法主要基于Unicode标准:Unicode是一种字符编码标准,它为世界上几乎所有的字符提供了唯一的编码值。glibc使用Unicode标准作为内部字符表示,以实现不同字符集之间的转换。

● Python的codecs模块

codecs是Python标准库中的一个模块,用于字符编码和解码操作。它提供了一组函数和类,用于在不同的字符编码之间进行转换。在处理文本数据时,经常需要将文本从一种编码格式转换为另一种编码格式。这可能涉及到将文本从Unicode转换为其他编码(如UTF-8、ASCII等),或者将文本从其他编码转换为Unicode。codecs模块提供了一种简单而一致的方式来执行这些编码和解码操作。

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

编码和解码函数:codecs模块提供了一组函数,如codecs.encode()和codecs.decode(),用于执行字符编码和解码操作。这些函数接受输入文本和目标编码格式作为参数,并返回编码或解码后的文本。

多种编码支持:codecs模块支持许多常见的字符编码格式,包括ASCII、UTF-8、UTF-16、UTF-32等。它还提供了对其他编码格式的支持,如Base64、Quoted-Printable、ROT13等。

错误处理:在进行字符编码和解码时,可能会出现无法处理的字符或编码错误。codecs 模块允许指定不同的错误处理策略,以处理这些错误情况。例如,可以选择忽略无法处理的字符,替换它们或引发异常。

使用codecs模块,可以便捷地进行不同编码之间的转换,处理文本数据的编码问题,并确保数据在不同环境中正确地传输和解释。

4. 项目实施中字符集转换介绍

以TERADATA(TD)数据库迁移到高斯数据库为例,一般TD数据库默认是使用latin1的字符集,而应用一般使用中文GBK字符集在TD数据库中存储数据,所以当从TD数据库迁移到其他数据库时,应该以GBK字符集作为源数据库字符集。

数据迁移主要流程如下:

■从TD数据库中导出数据并以GBK字符集落地为数据文件。

■将GBK数据文件转换为UTF8文件。

■将UTF8数据文件导入到高斯数据库(高斯数据库的外表加载也可以将GBK字符集转换为UTF8字符集,在此不做讨论)

某证券公司的业务表部分示例数据如下,从TD数据库中导出的数据是GBK字符集,数据中有3个字段,字段分隔符为:||,数据的第三个字段是中文。在迁移过程中中文字段可能会存在乱码,所以在使用不同的字符集转换方式后其转换的结果也会有所不同。

示例数据中第一行的第三个中文字段有乱码,正确的数据如下:

G000A||10000||广东省广州市天河区天河北路437号

E000D||20000||上海市浦东新区来安路685号

Q000D||20000||山东省青岛市崂山区仙霞岭路17~21号

第一行中文字段的GBK十六进制编码如下:

图片

数据中“州”字的GBK编码:D6 DD,但是实际的数据中由于某种原因造成D6丢失,由于GBK是双字节编码,所以DD和后面的字节(CA)重新组成了另一个汉字:菔,而以此类推后面的汉字,每两个字节组成一个汉字,但B7 34在GBK编码中不能组成汉字,34在GBK编码中是:4,也正是“437号”中的“4”。

图片

当使用iconv转换此带有乱码的GBK文件时,效果如下所示。

iconv系统内核版本、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

转换命令如下:

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

所以经过iconv转换后,B7和34不能组成汉字,所以B7被丢弃,而实际的内容如下:

G000A||10000||广东省广菔刑旌忧旌颖甭437号

E000D||20000||上海市浦东新区来安路685号

Q000D||20000||山东省青岛市崂山区仙霞岭路17~21号

当python程序使用内置库codecs进行代码转换后,可以有2个参数选项errors='replace'和errors='ignore',‘replace’表示当出现乱码后可以把乱码替换成“?”,而'ignore'表示当出现乱码后,会把乱码丢弃(和iconv特性相同)。

当使用codecs做代码转换时,使用'replace'参数,部分代码如下:

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

转换后的结果如下:

G000A||10000||广东省广�菔刑旌忧�天河北路437号

E000D||20000||上海市浦东新区来安路685号

Q000D||20000||山东省青岛市崂山区仙霞岭路17~21号

当使用codecs做代码转换时,使用'ignore'参数,部分代码如下:

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

转换后的结果如下:

G000A||10000||广东省广菔刑旌忧天河北路437号

E000D||20000||上海市浦东新区来安路685号

Q000D||20000||山东省青岛市崂山区仙霞岭路17~21号

5. 总结

■ iconv 2.17版本就是根据glibc库进行字符集转换,不能转换的就丢弃,且当文件中有半个字节丢失后,后面转换的中文字符很可能是不准确的。如在本示例中,遇到乱码后,最终转换的字符为:“菔刑旌忧旌颖甭437号”

■ Python的内置库codecs对中文转换时采用一种“转换最多中文字符”的策略,所以codecs在本示例中,遇到乱码后,最终转换的字符为:“菔刑旌忧天河北路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数据文件(使用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数据文件{0},报错信息{1},程序异常退出!!".format(fileGbkAPName,e))
        sys.exit(-1)


    tmpGbkCont = gbkFileStream.readlines()


    # 转换为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("程序执行非法调用,异常退出!!")
    sys.exit(-1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/438199.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

武汉灰京文化:游戏推广的领军者

在当今飞速发展的游戏行业中,游戏推广成为了每个游戏开发商和发行商必然要面对的挑战。如何能够将游戏信息传播给更广泛的受众群体,提升游戏的知名度和用户参与度,成为了每个游戏从业者需要思考的问题。而武汉灰京文化作为游戏推广领域的领军…

如何减少AI中的偏见问题:八种方法避免AI偏见渗入模型

克服与避免 AI 偏见的八大方法 AI 中的算法偏见是一个普遍存在的问题,它虽然不可能完全消除,但却可以通过科学的方法积极地防止这种偏见。我们将在本文中围绕如何应对AI中的偏见问题展开深入的讨论。 您可能会回想起新闻中报道的一些存在偏见的算法示例…

springboot248校园资产管理

校园资产管理 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本校园资产管理就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大…

试用Claude3

1 简介 好消息是,2024 年 3 月 4 日发布了 Claude3,据传比 GPT-4 更好,snooet 版本可以免费试用,坏消息是我们这儿不能用。 在官网注册时,需要选择国家并使用手机接收短信验证码。而在选项中没有中国这个选项。即使成…

【Maven】Maven 基础教程(五): jar 包冲突问题

《Maven 基础教程》系列,包含以下 5 篇文章: Maven 基础教程(一):基础介绍、开发环境配置Maven 基础教程(二):Maven 的使用Maven 基础教程(三):b…

贪心 Leetcode 763 划分字母区间

划分字母区间 Leetcode 763 学习记录自代码随想录 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。 注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。 返…

JAVA语言基础 JAVA入门

注释 单行注释:用双斜线 // 表示 多行注释:用 /*------------------*/ 表示 文档注释:用 /**-----------------*/ 表示 分隔符 常见的分隔符有:分号 ; 花括号 {} 方括号 [ ] 圆括号 () 空格 圆点 . 在 Java 语言中每一条…

LeetCode 刷题 [C++] 第300题.最长递增子序列

题目描述 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 题目…

快递包装展|2024上海国际电商物流包装产业展览会

2024中国(上海)国际电商物流包装产业展览会 2024 China (Shanghai) international e-commerce logistics packaging industry exhibition 时 间:2024年7月24日 —7月26日 地 点:国家会展中心(上海市青浦区崧泽大道333号&#xff…

react 分步表单中使用useEffect来更新表单值的问题

问题背景:我在完成一个分步表单的功能的时候,在进行点击下一步的时候,会通过useEffect 来监听下一步或者上一步的动作,进行表单赋值,我使用 useEffect(() > {setFieldsValue(formValues);}, [stepNum]) 直接赋值的…

2024-3-7 市场分歧视角

昨天安奈儿市场带领市场情绪一致,新型工业化方向独占鳌头,日内高潮节点尾盘老龙 克来机电涨停,昨晚很多老师在YY老龙是不是要二波了,呵呵。 今天市场分歧从竞价就开始了,隔夜单我记忆中 天奇股份88亿,上海…

MySQL--优化(索引--索引创建原则)

MySQL–优化(索引–索引创建原则) 定位慢查询SQL执行计划索引 存储引擎索引底层数据结构聚簇和非聚簇索引索引创建原则索引失效场景 SQL优化经验 一、索引创建原则 我们使用的索引种类: 主键索引唯一索引根据业务创建的索引(复…

线程安全——使用线程安全函数,多线程中执行fork引发的问题及如何解决

目录 一、引例 二、线程安全 三、多线程中执行fork 3.1 多线程中某个线程调用 fork()&#xff0c;子进程会有和父进程相同数量的线程吗? 3.2 父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁 一、引例 在主线程和函数线程中进行语句分割并输出。 #include <stdi…

CRichEditUI中文乱码问题(Duilib)

这是遇到问题的时候&#xff0c;我还以为是韩文 解决方案&#xff1a; //HMODULE hmod LoadLibrary(_T("msftedit.dll"));HMODULE hmod LoadLibrary(_T("riched20.dll"));//修改一下使用的动态库&#xff0c;兼容性问题需要自己测

每日OJ题_链表②_力扣24. 两两交换链表中的节点

目录 力扣24. 两两交换链表中的节点 解析代码 力扣24. 两两交换链表中的节点 24. 两两交换链表中的节点 难度 中等 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&…

JavaWeb04-Request,Response

目录 一、Request&#xff08;请求&#xff09; 1.作用 2.继承体系 3.获取请求数据 &#xff08;1&#xff09;请求行 &#xff08;2&#xff09;请求头 &#xff08;3&#xff09;请求体&#xff08;POST&#xff09; &#xff08;5&#xff09;Request通用方式获取请求…

植物神经紊乱的五大信号,你知道吗?

植物神经紊乱&#xff0c;听起来像是医学名词&#xff0c;但其实它离我们的生活并不遥远。它就像一位隐形的朋友&#xff0c;时常悄悄地出现&#xff0c;给我们带来从头到脚的不适&#xff0c;让我们的生活变得困扰不已。今天&#xff0c;就让我们一起揭开这位“朋友”的真面目…

[Unity实战]使用NavMeshAgent做玩家移动

其实除了Character Controller, Rigidbody&#xff0c;我们还可以使用NavMeshAgent去做。这么做的好处是能避免玩家去莫名其妙的地方&#xff08;毕竟基于烘焙过的导航网格&#xff09;&#xff0c;一般常见于元宇宙应用和mmo。 根据Unity手册&#xff0c;NavMeshAgent 也有和…

【JavaEE初阶 -- 计算机核心工作机制】

这里写目录标题 1.冯诺依曼体系2.CPU是怎么构成的3.指令表4.CPU执行代码的方式5.CPU小结&#xff1a;6.编程语言和操作系统7. 进程/任务&#xff08;Process/Task&#xff09;8.进程在系统中是如何管理的9. CPU分配 -- 进程调度10.内存分配 -- 内存管理11.进程间通信 1.冯诺依曼…

QPaint绘制自定义仪表盘组件04

网上视频抄的&#xff0c;用来自己看一下&#xff0c;看完就删掉 最终效果 ui widgetspeed.h #ifndef WIDGETSPEED_H #define WIDGETSPEED_H#include <QWidget> #include <QPaintEvent> #include <QPainter> #include <QDebug> #include <QFont&g…