技术实践|数据迁移中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)
技术实践|数据迁移中GBK转UTF8字符集问题分析的更多相关文章
- Golang中GBK和UTF8编码格式互转
Golang中GBK和UTF8编码格式互转 需求 已知byte数组的编码格式转换 实现代码 package utils import ( "bytes" "golang. ...
- PHP通过iconv将字符串从GBK转换为UTF8字符集
PHP通过iconv将字符串从GBK转换为UTF8字符集的方法,需要的朋友可以参考下. 1. iconv()介绍 iconv函数可以将一种已知的字符集文件转换成另一种已知的字符集文件.例如:从GB23 ...
- PHP中GBK和UTF8乱码解决方案
我用的appserv-win32-2.5.10做的环境,装这个包的时候用默认的utf8编码.在写数据库连接文件时,写成: $conn = mysql_connect("$host" ...
- VC中GBK与UTF8转化
void ConvertGBKToUtf8(CString& strGBK) { int len=MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)strG ...
- 函数:PHP将字符串从GBK转换为UTF8字符集iconv
1. iconv()介绍 iconv函数可以将一种已知的字符集文件转换成另一种已知的字符集文件.例如:从GB2312转换为UTF-8. iconv函数在php5中内置,GB字符集默认打开. 2. ic ...
- EF 中 Code First 的数据迁移以及创建视图
写在前面: EF 中 Code First 的数据迁移网上有很多资料,我这份并没什么特别.Code First 创建视图网上也有很多资料,但好像很麻烦,而且亲测好像是无效的方法(可能是我太笨,没搞成功 ...
- 【odoo】[经验分享]数据迁移注意事项
[odoo14]经典好书学习没有烂尾,主体已完成,可移步了解.https://www.cnblogs.com/xushuotec/p/14428210.html 背景 近期,有朋友打算上odoo系统. ...
- HDFS数据迁移解决方案之DistCp工具的巧妙使用
前言 在当今每日信息量巨大的社会中,源源不断的数据需要被安全的存储.等到数据的规模越来越大的时候,也许瓶颈就来了,没有存储空间了.这时候怎么办,你也许会说,加机器解决,显然这是一个很简单直接但是又显得 ...
- nodejs简单数据迁移demo
近期做数据迁移,采用nodejs框架,数据库为mysql.作为一枚菜鸟,在编码过程中,遇到众多奇葩问题,感谢民少给予的支持. 由于旧数据库中的数据,在之前设计中存在众多不合理的情况,因此在数据迁移中, ...
- 阿里云RDS实例内不同数据库之间的数据迁移
适用场景 本文适用于使用DTS实现相同实例下库名不同的数据库之间的数据迁移.本文以使用DTS将同一RDS实例下的amptest库迁移到jiangliu_amptest库为例来说明如何使用DTS实现相同 ...
随机推荐
- 使用MessagePipe实现进程间通信
1.MessagePipe介绍 可以用于.NET和Unity上面的高性能的内存/分布式消息传递管道.适用于发布/订阅模式.CQRS的中介模式.Prism中的EventAggregator.IPC(进程 ...
- c++中的读写锁
读写锁是一种特殊的锁机制,允许多个线程同时读取共享数据,但在写入共享数据时,只有一个线程可以进行写操作,其他线程必须等待. 这种机制对于读多写少的场景非常有效,可以提高并发性能.以下是通过 share ...
- Android 12 关机重启流程
1. 关机流程 Android上层触发关机的入口很多,但最终几乎都是调用ShutdownThread.shutdown来实现.如下是一些常见的调用关机的点: StatusBarManagerServi ...
- ES5 和 ES6 的区别,说几个 ES6 的新增方法
ECMAscript5.,即ES5 ,表示 ECMAscript的第五次修订-2009 : ECMAscript6.,即ES6 ,表示 ECMAscript的第六次修订-2015 : ES6 是对于 ...
- ADO.NET 连接数据库 【vs2022 + sqlServer】
using System.Data; using System.Data.SqlClient; namespace Zhu.ADO.NET { internal class Program { pri ...
- 05 Transformer 中的前馈神经网络(FFN)的实现
2:20:理论链接 博客配套视频链接: https://space.bilibili.com/383551518?spm_id_from=333.1007.0.0 b 站直接看 配套 github 链 ...
- KubeSphere 社区双周报|2024.04.26-05.09
KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书.新增的讲师证书以及两周内提交过 commit 的贡献者,并对近期重要的 PR 进行解析,同时还包含了线上/线下活动和布道推广等一系列 ...
- KubeSphere 社区双周报 | Fluent Operator 发布 v2.5.0 | 2023.09.01-09.14
KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书.新增的讲师证书以及两周内提交过 commit 的贡献者,并对近期重要的 PR 进行解析,同时还包含了线上/线下活动和布道推广等一系列 ...
- 运营商业务系统基于 KubeSphere 的容器化实践
本篇文章是 KubeSphere 2020 年度 Meetup 上讲师宋磊分享内容整理而成. 大家好,我是宋磊,在运营商的一个科技子公司任职,主要做大数据业务.我主要负责公司的 IaaS 层和 Paa ...
- DataDream:调一调更好,基于LoRA微调SD的训练集合成新方案 | ECCV'24
尽管文本到图像的扩散模型已被证明在图像合成方面达到了最先进的结果,但它们尚未证明在下游应用中的有效性.先前的研究提出了在有限的真实数据访问下为图像分类器训练生成数据的方法.然而,这些方法在生成内部分布 ...