-
彻底解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)
2014-02-15 16:46:22默认编码:System(简体中文windows系统默认指的是GBK编码,即下拉框选项里的GBK/windows-936-2000/CP936/MS936/windows-936) 二、编码知识科普 Qt常见的两种编码是:UTF-8和GBK ★UTF-8:Unicode Transformat尊重作者,支持原创,如需转载,请附上原地址:https://blog.csdn.net/libaineu2004/article/details/19245205
这篇文章有点长,内容有点多,如果时间急迫,可以直接翻页去末尾看结论。红色字体加粗的。(#^.^#)
一、Qt Creator环境设置
1、cpp或h文件从window上传到Ubuntu后会显示乱码,原因是因为ubuntu环境设置默认是utf-8,Windows默认都是GBK.
我们使用的Windows系统本地字符集编码为GBK。
2、Windows环境下,Qt Creator,菜单->工具->选项->文本编辑器->行为->文件编码->默认编码,常用的选项有以下几个:
System(简体中文windows系统默认指的是GBK编码)
GBK/windows-936-2000/CP936/MS936/windows-936
UTF-8
二、编码知识科普
Qt常见的两种编码是:UTF-8和GBK
★UTF-8:Unicode TransformationFormat-8bit,允许含BOM,但通常不含BOM。是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24为(三个字节)来编码。UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。UTF-8编码的文字可以在各国支持UTF8字符集的浏览器上显示。如果是UTF8编码,则在外国人的英文IE上也能显示中文,他们无需下载IE的中文语言支持包。
★GBK是国家标准GB2312基础上扩容后兼容GB2312的标准。GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBD大。GBK是GB2312的扩展,除了兼容GB2312外,它还能显示繁体中文,还有日文的假名。
★GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换:
GBK、GB2312--Unicode--UTF8
UTF8--Unicode--GBK、GB2312
★在简体中文windows系统下,ANSI编码代表GBK/GB2312编码,ANSI通常使用0x80~0xFF范围的2个字节来表示1个中文字符。0x00~0x7F之间的字符,依旧是1个字节代表1个字符。Unicode(UTF-16)编码则所有字符都用2个字节表示。三、编码转换
Windows自带的记事本,无法查看UTF-8编码的文件到底有无BOM,需要使用其他文件编辑器,比如EditPlus或者SublimeText。
UTF-8与ANSI(即GBK)的互转,可以使用EditPlus工具"文件另存为"或者Encodersoft编码转换工具对.cpp和.h源文件文本进行批量转换.四、QString显示中文乱码的原因
我们使用的Windows系统本地字符编码(Local字符集)为GBK。编译器分析出源文件字符编码之后,会进行解码再编码,将源字符集转码成执行字符集。执行字符集一般默认为使用本地字符编码(Local字符集)。
Qt5可以设置Local字符集,GBK/UTF-8
QTextCodec *codec = QTextCodec::codecForName("UTF-8");//或者"GBK",不分大小写 QTextCodec::setCodecForLocale(codec);
Qt5中QString内部采用unicode字符集,utf-16编码。构造函数QString::QString(const char *str)默认使用fromUtf8(),将str所指的执行字符集从utf-8转码成utf-16。
由上面fromUtf8()可知,QString需要执行字符集编码为utf-8,然后以utf-8进行解码,再编码为utf-16才能获得正确的字符编码。显示中文乱码的原因其实就是QString转码方式与执行字符集不一致。(比如,源字符集为本地字符集GBK编码,QString以utf-8的方式进行解码,会导致获得错误的二进制编码,再将错误二进制转为utf-16就会出现乱码。)
五、Qt编码指定
Qt需要在main()函数指定使用的字符编码:
#include <QTextCodec> int main(int argc, char *argv[]) { QApplication a(argc, argv); //设置中文字体 a.setFont(QFont("Microsoft Yahei", 9)); //设置中文编码 #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0)) #if _MSC_VER QTextCodec *codec = QTextCodec::codecForName("GBK"); #else QTextCodec *codec = QTextCodec::codecForName("UTF-8"); #endif QTextCodec::setCodecForLocale(codec); QTextCodec::setCodecForCStrings(codec); QTextCodec::setCodecForTr(codec); #else QTextCodec *codec = QTextCodec::codecForName("UTF-8"); QTextCodec::setCodecForLocale(codec); #endif return a.exec(); }
这里只列举大家最常用的3个编译器(微软VC++的cl编译器,Mingw中的g++,Linux下的g++),源代码分别采用GBK和无BOM的UTF-8以及有BOM的UTF-8这3种编码进行保存,发生的现象如下表所示。
情况1:指的是Local字符集为GBK
情况2:指的是Local字符集为UTF-8
-
源代码的编码
编译器
显示正常
显示乱码 GBK
win vs cl
情况1
情况2
win mingw-g++
情况1
情况2
linux g++
情况1
情况2 UTF-8(无BOM)
win vs cl
编译失败
error C2001: 常量中有换行符
编译失败
error C2001: 常量中有换行符win mingw-g++
情况2
情况1 linux g++
情况2
情况1
UTF-8(有BOM)
win vs cl
情况1
情况2(有#pragma预处理)
情况2(没有#pragma预处理) win mingw-g++
情况2
情况1 linux g++
情况2
情况1 - 如果您使用的是Visual C++编译器,则默认情况下不会将您的源代码视为utf-8编码。除非有BOM,否则它将使用您当前的代码页进行解释。就是说,当使用Visual C++编译程序的时候,它会分析源文件采用何种编码,有BOM标识符则可以正确识别其编码是UTF-8,若没有BOM标识符则认为其使用本地字符集编码(Local字符集)。Local字符集是什么?取决于你的设置QTextCodec *codec = QTextCodec::codecForName(???);
- 如果源文件是UTF-8+BOM的编码方式,还需要在头文件加入
#if defined(_MSC_VER) && (_MSC_VER >= 1600) # pragma execution_character_set("utf-8") #endif
或者添加
QMAKE_CXXFLAGS += /utf-8
到您的.pro文件中。- 如果源文件是UTF-8+无BOM的编码方式,则一定不能加#pragma execution_character_set(“utf-8”),不然会产生乱码。
六、测试案例
案例1、中文字符串测试
#if defined(_MSC_VER) && (_MSC_VER >= 1600) # pragma execution_character_set("utf-8") #endif #include <QApplication> #include <QTextCodec> #include <QPushButton> #include <QDebug> #include <QString> int main(int argc, char *argv[]) { QApplication a(argc, argv); //设置中文字体 a.setFont(QFont("Microsoft Yahei", 9)); //设置中文编码 #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0)) #if _MSC_VER QTextCodec *codec = QTextCodec::codecForName("gbk"); #else QTextCodec *codec = QTextCodec::codecForName("utf-8"); #endif QTextCodec::setCodecForLocale(codec); QTextCodec::setCodecForCStrings(codec); QTextCodec::setCodecForTr(codec); #else QTextCodec *codec = QTextCodec::codecForName("utf-8"); QTextCodec::setCodecForLocale(codec); #endif QString str(QObject::tr("1中文")); qDebug() << str; qDebug() << QStringLiteral("2中文"); qDebug() << QString::fromLatin1("3中文"); qDebug() << QString::fromLocal8Bit("4中文"); qDebug() << QString::fromUtf8("5中文"); qDebug() << QString::fromWCharArray(L"6中文"); return a.exec(); }
当QTextCodec::codecForName("utf-8");时,
QString::fromLocal8Bit和QString::fromUtf8是等效的。
当QTextCodec::codecForName("gbk");时,
QString::fromLocal8Bit和QString::fromUtf8是不等效的。
案例2、QCom跨平台串口调试助手(http://www.qter.org/?page_id=203)
源代码qcom\mainwindow.cpp,aboutdialog.cpp等文件用的是UTF-8编码(无BOM);但是qcom\qextserial\*.*文件用的是ANSI编码.在linux环境编译完全OK.
笔者Windows环境的Qt Creator+微软VC++编译器,环境设置用的是ANSI(即GBK)编码.编译源文件会报错.
错误提示"fatal error C1018: 意外的 #elif".
解决方法由两种:方法1:
把qcom\的所有cpp和h文件都用工具转换成ANSI编码,main()函数使用QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));
方法2:
先把Qt Creator环境设置用的是UTF-8编码,
再把qcom\的所有cpp和h文件都用工具转换成UTF-8+BOM编码,请注意,如果文件转换成UTF-8(无BOM),编译仍会失败.main()函数使用QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));//注意,此处仍是"GBK",不是"UTF-8"
重新编译,OK!其它:
七、结论
Windows环境下,Qt Creator+微软VC++编译器,新建工程,
1、如果该工程不需要跨平台使用(只在win),那么工程设置请使用GBK的编码方式.
2、如果该工程要跨平台使用(win+linux),那么工程设置请使用UTF-8+BOM的编码方式.
另外,还需要在预编译头文件加入
#if defined(_MSC_VER) && (_MSC_VER >= 1600) # pragma execution_character_set("utf-8") #endif
或者添加
QMAKE_CXXFLAGS += /utf-8
到您的.pro文件中。3、Linux环境下,Qt Creator+gcc,新建工程,
没有GBK编码可选,默认是UTF-8(无BOM)编码方式,考虑到跨平台,建议选择UTF-8+BOM的编码方式.
★★★★★★★★★★★★★★★综上所述,解决乱码的方法概括如下:★★★★★★★★★★★★★★★★★★★★★
1、如果IDE是Qt Creator,把它的环境设置为“UTF-8+BOM”编码。
2、如果IDE是Visual Studio,请下载插件,名称是ForceUTF8 (with BOM),所有源文件和头文件都会保存为“UTF-8+BOM”编码。
3、如果编译器是MSVC,请在预编译头stdafx.h文件加入
#if defined(_MSC_VER) && (_MSC_VER >= 1600)
# pragma execution_character_set("utf-8")
#endif4、源码文件main函数入口设置中文编码:
#include <QTextCodec>
QApplication a(argc, argv);
//设置中文字体
a.setFont(QFont("Microsoft Yahei", 9));//设置中文编码
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
#if _MSC_VER
QTextCodec *codec = QTextCodec::codecForName("gbk");
#else
QTextCodec *codec = QTextCodec::codecForName("utf-8");
#endif
QTextCodec::setCodecForLocale(codec);
QTextCodec::setCodecForCStrings(codec);
QTextCodec::setCodecForTr(codec);
#else
QTextCodec *codec = QTextCodec::codecForName("utf-8");
QTextCodec::setCodecForLocale(codec);
#endif5、如此一来,不管是MSVC编译器还是MinGW编译器,都能编译通过,且支持中文!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
x、参考文献
Qt官网文档
https://wiki.qt.io/Strings_and_encodings_in_Qt
https://doc.qt.io/qt-5/unicode.html
ASCII,Unicode和UTF-8完全搞清楚 https://blog.csdn.net/Deft_MKJing/article/details/79460485
Qt中文乱码原因及解决方案 https://blog.csdn.net/qq_35905572/article/details/95042444
Qt中文乱码问题 http://blog.csdn.net/brave_heart_lxl/article/details/7186631
尊重作者,支持原创,如需转载,请附上原地址:https://blog.csdn.net/libaineu2004/article/details/19245205
-
-
IntelliJ IDEA 统一设置编码为utf-8编码
2018-06-11 10:49:11问题一:File->Settings->Editor->File Encodings问题二:...File Encodings问题三:将项目中的.idea文件夹中的encodings.xml文件中的编码格式改为uft-8问题四:File->Settings-&...问题一:
File->Settings->Editor->File Encodings
问题二:
File->Other Settings->Default Settings ->Editor->File Encodings
问题三:将项目中的.idea文件夹中的encodings.xml文件中的编码格式改为uft-8
问题四:
File->Settings->Build,Execution,Deployment -> Compiler -> Java Compiler
设置 Additional command line parameters选项为 -encoding utf-8
问题五:
1)打开Run/Debug Configuration,选择你的tomcat
2) 然后在 Server > VM options 设置为 -Dfile.encoding=UTF-8 ,重启tomcat
问题六:
清空浏览器缓存再试一次。
-
拨开字符编码的迷雾--字符编码概述
2017-11-27 20:46:23相信不少人在字符编码上面摔过跟头,这篇文章针对开发中需要了解的字符编码知识进行了简要的讲解,希望能够对大家有所帮助。 1. ASCII及其扩展 1.1 什么是ASCII字符集 字符集就是一系列用于显示的字符的...为什么这样的字符串
{"data":"颸颸"}
,JSON库(如jsoncpp
)会解析失败?
为什么界面上韩文显示乱码?
ASCII和ANSI有什么区别?
相信不少人在字符编码上面摔过跟头,这篇文章针对开发中需要了解的字符编码知识进行了简要的讲解,希望能够对大家有所帮助。1. ASCII及其扩展
1.1 什么是ASCII字符集
字符集就是一系列用于显示的字符的集合。ASCII字符集由美国国家标准协会(American National Standard Institute)于1968年制定一个字符映射集合。
ASCII使用7位二进制位来表示一个字符,总共可以表示128个字符(即
2^7
,二进制000 0000 ~ 111 1111
十进制0~127
)。ASCII字符集中每个数字对应一个唯一的字符,如下表:
因为其对应关系非常简单,不需要特殊的编码规则,所以严格来讲ASCII不能算字符编码,因为它没有规定编码规则。我们只是习惯将ASCII字符集称之为ASCII码、ASCII编码。
1.2 ASCII的扩展
1.2.1 最高位扩展 - ISO/IEC 8859
ASCII字符集是美国人发明的,这些字符完全是为其量身定制的。但随着计算机技术的发展和普及,传到了欧洲(如法国、德国)各国。由于欧洲很多国家中使用的字符除了ASCII表中的128个字符之外,还有一些各国特有的字符,于是欧洲人民发现ASCII字符集表达不了他们所要表达的东西呀。怎么办了?他们发现ASCII只使用了一个字节(8位)之中的低7位,于是欧洲各国开始各显神通,打起了那1个最高位(第0位)的主意,将最高位利用了起来,这样又多了128个字符,从而满足了欧洲人民的需要。
但因为每个国家的需求不一样,各国都设计了不同的方案。为了结束这种混乱的局面,国际标准化组织(ISO)及国际电工委员会(IEC)联合制定了一系列8位字符集的标准,统称为ISO 8859(全称ISO/IEC 8859)。注意,这是一系列字符集的统称。如ISO/IEC 8859-1(也就是常听到的Latin-1)支持西欧语言,ISO/IEC 8859-4(Latin-4)支持北欧语言等。
完整列表如下(摘自百度百科):
ISO/IEC 8859-1 (Latin-1) - 西欧语言
ISO/IEC 8859-2 (Latin-2) - 中欧语言
ISO/IEC 8859-3 (Latin-3) - 南欧语言,世界语也可用此字符集显示。
ISO/IEC 8859-4 (Latin-4) - 北欧语言
ISO/IEC 8859-5 (Cyrillic) - 斯拉夫语言
ISO/IEC 8859-6 (Arabic) - 阿拉伯语
ISO/IEC 8859-7 (Greek) - 希腊语
ISO/IEC 8859-8 (Hebrew) - 希伯来语(视觉顺序)
ISO 8859-8-I - 希伯来语(逻辑顺序)
ISO/IEC 8859-9 (Latin-5 或 Turkish) - 它把Latin-1的冰岛语字母换走,加入土耳其语字母。
ISO/IEC 8859-10 (Latin-6 或 Nordic) - 北日耳曼语支,用来代替Latin-4。
ISO/IEC 8859-11 (Thai) - 泰语,从泰国的 TIS620 标准字集演化而来。
ISO/IEC 8859-13 (Latin-7 或 Baltic Rim) - 波罗的语族
ISO/IEC 8859-14 (Latin-8 或 Celtic) - 凯尔特语族
ISO/IEC 8859-15 (Latin-9) - 西欧语言,加入Latin-1欠缺的芬兰语字母和大写法语重音字母,以及欧元符号。
ISO/IEC 8859-16 (Latin-10) - 东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。我们在数据库中常见到的Latin-1、2、5、7其实就是上面提到的针对特定语言的ASCII扩展字符集。
1.2.2 多字节扩展 - GB系列
前面讲到了,欧洲各国有效利用闲置的最高位,对ASCII字符集进行了扩展。可是欧洲人民没有想到的是(当然他们也不用想这么多),在大洋彼岸有着一个拥有五千年历史的伟大民族,她拥有着成千上万的汉字,1个字节显然不够表达如此深厚的文化底蕴。
于是当计算机引入到中国之初,国家技术监督局就设计了GB系列编码方案(GB=guo biao)。
GB编码方案使用2个字节来表达一个汉字。同时为了兼容ASCII编码,规定各个字节的最高位(首位)必须为1,从而避免了和最高位为0的ASCII字符集的冲突。GB系列字符集经历下面的几个发展过程:
编码名称 发布时间 字节数 汉字范围 GB2312 1980年 变字节(ASCII 1字节,汉字2个字节) 6763个汉字 GB13000 1993年第一版 变字节(ASCII 1字节,汉字2个字节) 20902个汉字 GBK Windows95中 2个字节 21886个汉字和图形符号(含GB2312,BIG5中所有字符) GB18030 2000年第一版 变字节(ASCII 1字节,汉字2个或4个字节) 27484个汉字 每一次迭代,支持的字符数量都会增加,而且每一次迭代都会保留之前版本支持的编码,所以做到了向上兼容。
1.2.3 全角与半角
因为汉字在显示器上的显示宽度要比英文字符的宽度要宽一倍,在一起排版显示时不太美观。所以GB编码不仅仅加入了汉字字符,而且包括了ASCII字符集中本来就有的数字、标点符号、字母等字符。这些被编入GB编码的数字、标点、字母在显示器上的显示宽度比ASCII字符集中的宽度宽一倍,所以前者称为全角字符,后者称为半角字符。
2. ANSI
2.1 ANSI与代码页
前面说到了世界各国针对ASCII的扩展方案(如欧洲的ISO/IEC 8859,中国的GB系列等),这些ASCII扩展编码方案的特点是:他们都兼容ASCII编码,但他们彼此之间是不兼容的。微软将这些编码方案统称为ANSI编码。故ANSI并不是特指某一种编码方案,只有知道了在哪个国家,哪个语言环境下,才知道它具体表示哪个编码方案。
在windows操作系统上,默认使用ANSI来保存文件。那么操作系统是如何知道ANSI到底应该表示哪种编码了,是GBK,还是ASCII,或者还是EUC-KR了? windows通过一个叫"Code Page"(翻译为中文就叫代码页)的东西来判断系统的默认编码。
简体中文操作系统默认的代码页是936,它表示ANSI使用的是GBK编码。
GB18030编码对应的windows代码页为CP54936。可以使用命令
chcp
来查看系统默认的代码页.汉字
(念suì,因为CSDN编辑器不支持该汉字,所以用图片代替,吐槽~~~)只包含在GB18030中,GB2312、GB13000、GBK中均不包含。默认情况下,在Visual Studio中输入该汉字,visual studio会使用CP936(即GBK)来保存代码文件,但如果在代码文件中输入该汉字,visual studio弹出如下提示要求用户选择代码页:
2.2 更改默认代码页
2.2.1 chcp命令
可以使用
chcp
命令来更改默认代码页,如chcp 437
将默认代码页更改为437(美国)。2.2.2 控制面板
在“控制面板”–>“区域和语言”–> “更改系统区域设置”中更改系统默认的代码页为“中文(简体,中国)”。
2.2.3 代码修改
也可以通过代码更改默认的代码页:
char *setlocale( int category, const char *locale );
3. Unicode
3.1 Unicode产生背景
各个国家使用不同的编码规则,虽然他们都是兼容ASCII的,但它们相互却是不兼容的。
试想法国人Jack写了一封名为"love_you.txt"的信,传给了他的德国朋友Rose,Rose想要在windows系统上打开这个文件,她需要知道德国使用的字符编码是Latin-1,然后还要确保她的计算机上安装了该编码,才能顺利的打开这个文件。
如果上面这些还能忍受,那么随着网络的发展,你从互联网上获取的文件,你很有可能不知道它来自哪个国家,使用的哪种编码。这也就是Email刚诞生时常常出现乱码的原因,发信人和收信人使用的编码可能是不一样的。于是
The Unicode Standard
(统一码标准)横空出世,它由The Unicode Consortium于1991年发布,我们习惯称它为Unicode字符集。Unicode字符集和ASCII字符集一样,也只是一个字符集合,标记着字符和数字之间的映射关系,它不包含任何编码规则和方案。和ASCII不一样的是,Unicode字符集支持的字符数量是没有限制的(具体可以参考Unicode规范)。
我们通常认为的Unicode字符固定占用2个字节的观点是错误的。如
(念suì)Unicode码为
D852 DF62
。那么Unicode字符是怎样被编码成内存中的字节的了?它是通过UTF(Unicode Transformation Formats)实现的,比较常见得有UTF-8,UTF-16。
在windows系统上汉字默认使用CP936(即GBK编码),占2个字节。而大多数Unicode字符的Unicode码值也占2个字节,所以大多数人误以为汉字字符串在内存中的值就是Unicode值,这是错误的。
可以从 站长工具-Unicode 查询汉字的Unicode码值。3.3 字符集与字符编码的区别
从ASCII、GB2312、GBK、GB18030、Big5(繁体中文)、Latin-1等采用的方案来看,它们都只是定义了单个字符与二进制数据的映射关系,一个字符在一个方案中只会存在一种表示方式,所以我们说GB2312是字符集还是字符编码方式都无所谓了。但是Unicode不一样,Unicode作为一个字符集可以采用多种编码方式,如UTF-8, UTF-16, UTF-32等。所以自Unicode出现之后,字符集与字符编码需要明确区分开来。
3.4 UTF-16编码的缺点
UTF-16编码方式规定用两个或四个字节来表示所有的字符。对于ASCII字符保持不变,只是将原来的7位扩展到了16位,其高9位永远是0。如字符’A’:
ASCII: 100 0001 UTF-16: 0000 0000 0100 0001
可以看到对于ASCII字符,UTF-16的存储空间扩大了一倍,UTF-16并不是完全兼容ASCII字符集。这对于那些ASCII字符集已经满足需求的西方国家来说完全是没必要的,而且ASCII字符经过UTF-16编码之后高字节始终是0,导致很多C语言函数(如
strcpy
,strlen
)会将此字节视为字符串的结束符'\0'
,从而出现错误的计算结果。
而且,UTF-16还存在大小端的问题,(念suì)Unicode码在大端系统上为
D852 DF62
,小端系统上为52D8 62DF
。
因此,UTF-16一开始推出的时候就遭到很多西方国家的抵制,影响了Unicode的推行。于是后来又设计了UTF-8编码方式,才解决了这些问题。3.5. Unicode字符集常用编码方式:UTF-8
3.5.1 UTF-8概述
UTF-8是互联网上使用最广泛的Unicode字符集编码方式。UTF-8编码的最小单位由8位(1个字节)组成,UTF-8使用一个至四个字节来表示Unicode字符。另外,UTF-8是完美兼容ASCII字符集的,这一点可以通过下面的UTF-8的编码规则得到证明。
3.5.2 UTF-8编码规则
UTF-8编码规则很简单:
(1)对于ASCII(单字节字符)字符,采用和ASCII相同的编码方式,即只使用一个字节表示,且该字节第一位为0.
(2)对于多字节(2~4字节)字符,假设字节数为n(1 < n <= 4),第一个字节:前n位都设为1,第n+1位设为0;后面的n-1个字节的前两位一律设为10。所有字节中的没有提及的其他二进制位,全部为这个符号的unicode码。3.5.2 UTF-8 BOM
BOM(byte order mark)从字面意义来看是标记字节顺序的。最早出现的原因是因为UTF-16和UTF-32编码采用2个或4个字节表示一个字符,面临大小端的问题。为了区分是使用的大端(Big Endian,简称BE)还是小端(Little Endian,简称LE),采用了在串的前面加入指定的字节加以区分,UTF-16大端加入
FE FF
,小端加入FF FE
. 比如, 字符串“ABC”的UTF-16编码为00 41 00 42 00 43
,对应的各种的字节序列如下:因为UTF-8和ASCII都是单字节序列,二者不好区分,微软采用在UTF-8编码的字符串前也加入BOM(3个字节
EF BB BF
)来标记UTF-8编码的串。UTF-8 BOM这一规范大多在windows下被使用,在其他平台下用的很少使用,如:Linux全部采用UTF-8编码,不存在要区分的情况;HTTP协议中可以包含Content-Type:text/html; charset=utf-8
这样的说明,也不需要区分。 -
一篇文章彻底弄懂Base64编码原理
2018-08-16 07:42:09目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一。在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进行(或再次Base64)传输。那么,...在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现。
Base64的由来
目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一。在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进行(或再次Base64)传输。那么,Base64到底起到什么作用呢?
在参数传输的过程中经常遇到的一种情况:使用全英文的没问题,但一旦涉及到中文就会出现乱码情况。与此类似,网络上传输的字符并不全是可打印的字符,比如二进制文件、图片等。Base64的出现就是为了解决此问题,它是基于64个可打印的字符来表示二进制的数据的一种方法。
电子邮件刚问世的时候,只能传输英文,但后来随着用户的增加,中文、日文等文字的用户也有需求,但这些字符并不能被服务器或网关有效处理,因此Base64就登场了。随之,Base64在URL、Cookie、网页传输少量二进制文件中也有相应的使用。
Base64的编码原理
Base64的原理比较简单,每当我们使用Base64时都会先定义一个类似这样的数组:
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
上面就是Base64的索引表,字符选用了"A-Z、a-z、0-9、+、/" 64个可打印字符,这是标准的Base64协议规定。在日常使用中我们还会看到“=”或“==”号出现在Base64的编码结果中,“=”在此是作为填充字符出现,后面会讲到。
具体转换步骤
- 第一步,将待转换的字符串每三个字节分为一组,每个字节占8bit,那么共有24个二进制位。
- 第二步,将上面的24个二进制位每6个一组,共分为4组。
- 第三步,在每组前面添加两个0,每组由6个变为8个二进制位,总共32个二进制位,即四个字节。
- 第四步,根据Base64编码对照表(见下图)获得对应的值。
0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w 15 P 32 g 49 x 16 Q 33 h 50 y
从上面的步骤我们发现:
- Base64字符表中的字符原本用6个bit就可以表示,现在前面添加2个0,变为8个bit,会造成一定的浪费。因此,Base64编码之后的文本,要比原文大约三分之一。
- 为什么使用3个字节一组呢?因为6和8的最小公倍数为24,三个字节正好24个二进制位,每6个bit位一组,恰好能够分为4组。
示例说明
以下图的表格为示例,我们具体分析一下整个过程。
- 第一步:“M”、“a”、"n"对应的ASCII码值分别为77,97,110,对应的二进制值是01001101、01100001、01101110。如图第二三行所示,由此组成一个24位的二进制字符串。
- 第二步:如图红色框,将24位每6位二进制位一组分成四组。
- 第三步:在上面每一组前面补两个0,扩展成32个二进制位,此时变为四个字节:00010011、00010110、00000101、00101110。分别对应的值(Base64编码索引)为:19、22、5、46。
- 第四步:用上面的值在Base64编码表中进行查找,分别对应:T、W、F、u。因此“Man”Base64编码之后就变为:TWFu。
位数不足情况
上面是按照三个字节来举例说明的,如果字节数不足三个,那么该如何处理?
- 两个字节:两个字节共16个二进制位,依旧按照规则进行分组。此时总共16个二进制位,每6个一组,则第三组缺少2位,用0补齐,得到三个Base64编码,第四组完全没有数据则用“=”补上。因此,上图中“BC”转换之后为“QKM=”;
- 一个字节:一个字节共8个二进制位,依旧按照规则进行分组。此时共8个二进制位,每6个一组,则第二组缺少4位,用0补齐,得到两个Base64编码,而后面两组没有对应数据,都用“=”补上。因此,上图中“A”转换之后为“QQ==”;
注意事项
- 大多数编码都是由字符串转化成二进制的过程,而Base64的编码则是从二进制转换为字符串。与常规恰恰相反,
- Base64编码主要用在传输、存储、表示二进制领域,不能算得上加密,只是无法直接看到明文。也可以通过打乱Base64编码来进行加密。
- 中文有多种编码(比如:utf-8、gb2312、gbk等),不同编码对应Base64编码结果都不一样。
延伸
上面我们已经看到了Base64就是用6位(2的6次幂就是64)表示字符,因此成为Base64。同理,Base32就是用5位,Base16就是用4位。大家可以按照上面的步骤进行演化一下。
Java 验证
最后,我们用一段Java代码来验证一下上面的转换结果:
package com.secbro2.blog.utils; import sun.misc.BASE64Encoder; /** * @author zzs */ public class Base64Utils { public static void main(String[] args) { String man = "Man"; String a = "A"; String bc = "BC"; BASE64Encoder encoder = new BASE64Encoder(); System.out.println("Man base64结果为:" + encoder.encode(man.getBytes())); System.out.println("BC base64结果为:" + encoder.encode(bc.getBytes())); System.out.println("A base64结果为:" + encoder.encode(a.getBytes())); } }
打印结果为:
Man base64结果为:TWFu BC base64结果为:QkM= A base64结果为:QQ==
以上结果与我们分析所得完全一致。
精品SpringBoot 2.x视频教程
《Spring Boot 2.x 视频教程全家桶》,精品Spring Boot 2.x视频教程,打造一套最全的Spring Boot 2.x视频教程。
程序新视界
公众号“程序新视界”,一个让你软实力、硬技术同步提升的平台
-
用R语言实现霍夫曼编码
2020-06-02 10:24:04可读性极低,而且其实也没必要用R语言写...mazijuzhen=matrix(,nrow=length(p),ncol=length(p)-1)###码字矩阵:第i行对应向量p的第i个分量所对应的那个待编码消息的编码后的码字 group=matrix(c(1:length(p),rep(NA,le. -
字符编码那些事--彻底理解掌握编码知识
2020-05-04 16:42:33每一个程序员都不可避免的遇到字符编码的问题,很多人在字符编码方面同样遇到不少问题,而且一直对各种编码懵懵懂懂、不清不楚。这篇文章就是针对字符编码中的一些问题进行了详细的阐述,能从根本上理解字符编码。 -
Visual Studio 2019修改编码UTF-8
2019-07-13 14:21:13Visual Studio 2019修改文件编码 最近在使用VS的时候遇到一个问题,以前可以通过编译的代码编译报错,提示内容如下所示 分析代码之后未发现有语法问题,于是根据C4819的警告,修改文件编码。 VS 2019隐藏了高级... -
idea快速编码之常用快捷键使用操作方法
2020-05-04 20:29:24学习时,用IDEA开发Java的时候,有不少经常使用到的快捷键,熟悉快捷键可以很好地提高编码速度。以下介绍idea常用的快捷键。 IDEA快捷键使用教程目录一、 日常开发常用快捷键二、 快速查找及替换三、 快速重构四、 ... -
IntelliJ IDEA 设置编码为utf-8编码
2018-06-08 21:58:59IntelliJ IDEA 统一设置为utf-8编码问题一:File->Settings->Editor->File Encodings问题二: -
emoji图片和编码表
2012-05-17 15:11:49467个emoji的文件和编码表数据 包括Unicode编码,UTF8编码,UTF16编码,SBUnicode编码 -
香农编码 哈夫曼编码 费诺编码的比较
2018-11-28 21:10:36文章目录哈夫曼编码编码步骤例子优点缺点费诺编码编码步骤例子优点缺点香农编码编码步骤例子优点缺点参考 备注:本文除了例子与数据,其他内容均为整合网络资源。 哈夫曼编码 编码步骤 S1 将信源符号按照概率大小从... -
熵编码:算术编码
2020-02-10 11:29:53算术编码不是简单的将每个信源符号映射成一个码字,而是对整个输入序列分配一个码字,所以平均意义上可以为每个信源符号分配长度小于1的码字。 算术编码操作简单,下面以一个实例讲解算术编码的原理: 设信源有a,b... -
汉字字符集编码查询 unicode编码查询
2018-09-13 09:04:06查询网址: ... 查询示例: ...常见的汉字字符集编码: ...GB2312编码:1981年5月1日发布的简体中文汉字编码国家标准。...BIG5编码:台湾地区繁体中文标准字符集,采用双字节编码,共收录13053... -
IDEA中Tomcat在控制台乱码问题以及IDEA编码设置UTF-8
2018-02-19 16:22:47在idea中经常遇到jsp的乱码问题,原因是编码不是UTF-8的问题,这次来彻底解决idea的编码问题首先设置idea编辑器的编码:File-Setting设置如下然后配置tomcat的编码问题:Run/DeBug Configurations在VM options填写-... -
ASCII码、Unicode编码对照表 —— ASCII控制字符 Unicode编码 字符编码的前世今生
2018-05-23 16:09:55ASCII控制字符 Unicode编码 ASCII(American Standard Code for Information Interchange,美国信息互换标准代码,ASCⅡ)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语和其他西欧语言。它是现今最... -
香农编码,哈夫曼编码与费诺编码的比较
2018-12-05 16:46:12一、香农编码 概念: 香农编码是是采用信源符号的累计概率分布函数来分配字码的。香农编码是根据香农第一定理直接得出的,指出了平均码长与信息之间的关系,同时也指出了可以通过编码使平均码长达到极限值。香农第一... -
信源编码与信道编码
2017-03-26 17:02:44但现代通信应用中常见的信源编码方式有:Huffman编码、算术编码、L-Z编码,这三种都是无损编码,另外还有一些有损的编码方式。信源编码的目标就是使信源减少冗余,更加有效、经济地传输,最常见的应用形式就是压缩。... -
[自编码器:理论+代码]:自编码器、栈式自编码器、欠完备自编码器、稀疏自编码器、去噪自编码器、卷积自...
2018-11-25 09:25:24自编码器及其变形很多,本篇博客目前主要基于普通自编码器、欠完备自编码器、稀疏自编码器和去噪自编码器,会提供理论+实践(有的理论本人没有完全理解,就先没有写上,后更)。另外,关于收缩自编码器、变分自编码... -
曼彻斯特编码,差分曼彻斯特编码和NRZI编码
2019-01-26 16:29:04曼彻斯特编码(Manchester Encoding),也叫做相位编码( Phase Encode,简写PE),是一个同步时钟编码技术,被物理层使用来编码一个同步位流的时钟和数据。 在曼彻斯特编码中,用电压的跳变的不同来区分1和0。 从低... -
彻底弄懂 Unicode 编码
2017-12-26 10:37:26今天,在学习 Node.js 中的 Buffer 对象时,注意到它的 alloc 和 from 方法会默认用 UTF-8 编码,在数组中每位对应 1 字节的十六进制数。想到了之间学习 ES6 时关于字符串的 Unicode 表示法,突然就很想知道 UTF-16 ... -
视频压缩编码和音频压缩编码的基本原理
2014-06-03 00:01:20本文介绍一下视频压缩编码和音频压缩编码的基本原理。其实有关视频和音频编码的原理的资料非常的多,但是自己一直也没有去归纳和总结一下,在这里简单总结一下,以作备忘。 -
python 中文转Unicode编码 & Unicode编码转中文
2019-05-28 13:32:50中文转Unicode编码: text.encode("unicode_escape") exp: # 中文转Unicode编码 text = "中国" res = text.encode("unicode_escape") # 输出结果 res = b'\\u4e2d\\u56fd' Unicode编码转中文: u.... -
prufer编码
2017-08-25 09:28:45prufer编码 prufer编码是用另外一种形式来描述一棵树,这棵树是无根树,它可以和无根树之间形成一一对应关系。 编码方式是: 这是一颗无根树,这课树的prufer编码为5,5,4,4,4,6。 首先选这棵树叶子中编号... -
硬编码和软编码
2018-06-11 17:36:49计算机科学中,只有硬编码(hardcode),以及非硬编码,有人也成为“软编码”。 硬编码和软编码的区别是:软编码可以在运行时确定,修改;而硬编码是不能够改变的。 java小例子: int a=2,b=2; 硬编码:if(a==2) ... -
哈夫曼编码最大编码长度
2019-02-07 00:42:46编码长度:第LLL层数据编码后的长度为LLL. 节点概率:若节点为叶子节点,则概率为叶子所编码数据的频率fif_ifi.或者一种不太严谨的方式,叶子节点的概率为所编码数据的概率pip_ipi.若节点为非叶子节点,概率为两个子... -
编码器
2019-07-04 16:49:57编码器是一种机电装备, 可以用来测量机械运动或者目标位置。 大多数编码器都使用光学传感器来提供脉冲序列形式的电信号, 这些信号可以依次转换成运动、方向或位置信息。 编码器依运动方式可分为旋转编码器或是线性... -
Eclipse设置编码格式
2018-02-25 09:27:32在开发过程中,我们常会遇到中文乱码问题,因此我们需要将编码格式设一致,一般将编码格式设置为UTF-8格式(根据各自的项目需要设置编码,一般项目大多用UTF-8编码)。下面我们将介绍一下使用Eclipse开发工具进行开发... -
硬编码与软编码
2018-11-07 17:23:18硬编码:就是将数据直接写入到代码中进行编译开发,比如在没有mybatits前,将sql语句写入到jdbc代码里,在比如纯jsp开发的...软编码:则是将数据与源代码解耦,比如mybatis的配置文件,将sql于底层代码分离,就只... -
中国 邮政编码四级结构,省(省无邮政编码)、市、区、街道邮政编码,在线api获取
2019-02-12 18:37:331. 2019 年中国邮政编码四级结构,省(省无邮政编码)、市、区、街道, 总共180w 2. 数据表结构 CREATE TABLE `post_code` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `province` varchar(255) comment ...
-
uni-app实战专题
-
二本学渣考研失败,一个本科渣渣是怎么逆袭从咸鱼到Offer收割机的?已拿offer
-
说说C语言在生活中的应用
-
【数据分析-随到随学】数据可视化
-
微服务系列第七十一季-Introducing Spring Boot
-
基于接口的动态代理
-
C/C++编程全家桶(Daozy极限编程)
-
转行做IT-第1章 计算机基础
-
第3章 入门程序、常量、变量
-
三维地图GIS大数据可视化
-
以iphone 6的视口为标准实现仿微信发现界面.zip
-
BUUCTF-WriteUp
-
排序算法之——简单选择排序
-
RabbitMQ消息中间件实战(附讲义和源码)
-
SubstancePainter插件开发-基础入门
-
WNMP安装一条龙step_by_step
-
国家注册渗透测试工程师(Web安全)
-
计算机组成之LD
-
关于虚拟机Linux网卡地址不显示以及虚拟机内外网不通解决办法
-
led_3_1.rar流水灯例子