乱码问题汇总

写在前面

在工作中经常会碰到各种莫名其妙的乱码问题,但通过之前的学习:字符集&字符编码-CSDN博客 ,可以知道乱码的根本原因就是使用和数据源编码不一样的编码解码导致。

如:BIG5解码GB2312编码内容,编解码不一致,必定会乱码。这时就需要进行字符编码的转换来使数据内容正常显示,之前的文章:字符编码转换-CSDN博客 里有详细介绍、实现了各种方式的字符编码转换的接口,可按实际情况找到相应的接口转换。

这里也记录下常见的乱码问题及解决方案供参考借鉴。

IDE(集成开发环境)软件显示乱码

IDE(集成开发环境)软件显示乱码,大多是因为文件编码格式和IDE打开文件使用的默认编码不一致,导致的显示乱码。

还原乱码

以VS为例,对于非UTF-8、UTF-16、UTF-32编码的文件,即ANSI编码的文件,VS会以系统本地默认(简体系统为GB2312,繁体系统为BIG5)编码打开文件,且不能更改-即不能指定使用何种编码打开文件。

现有一个GB2312编码的cpp文件:
1

现在在简体系统上,VS2019会以GB2312编码打开(解码)该GB2312编码的cpp文件,能够正常显示:
2

vs右下角显示当前打开文件编码,需在扩展安装FileEncoding插件:
3

当在繁体系统上,再用vs打开时,vs会自动以BIG5编码打开GB2312编码的文件(编码、解码格式不一致),从而发生乱码:
4

该文件的编码还是GB2312,只是VS以系统本地编码(BIG5)打开而已。

用notepad++打开,可以看到源文件编码还是GB2312:
5

因为微软没有提供修改vs文本编辑器打开文件的编码格式,因此要解决这种情况的乱码,只能修改文件本身的编码以适配vs文本编辑器的编码。将文件编码转换成通用的UTF-8或Unicode(UTF-16)编码即可。

这里也有多种方式转换文件编码如下:

解决方案一:使用VS转换文件编码

借助VS文件菜单栏 -> 高级保存选项转换:
6
7

8

解决方案二:使用Notepad++转换文件编码

借助Notepad++进行转换:
9

解决方案三:使用Windows记事本转换文件编码

使用Windows自带记事本转换:
10

11

注意:使用Windows记事本转换时需确保当前是正确解码打开的,若当前解码(显示)已乱码,后面只会越转越乱。

例:文件编码为GB2312,在繁体系统上(系统默认编码为BIG5)用记事本打开显示乱码(BIG5解码GB2312内容),这时再转只会越转越乱:
12

13

14

15

16

再用Notepad++打开,也无法再还原:

18

19

20

同样是打开文件乱码,为什么VS文本编辑器就可在乱码基础上转码成功并正常显示?

大概是记事本和VS在处理编码时有所不同造成的。VS有更强大的编码支持,可以识别并转换各种不同的字符编码格式,包括 GB2312 和 UTF-8。然而,记事本可能在处理一些非标准编码或特定类别的编码时会遇到更多的问题。记事本的编码支持可能不如专业的文本编辑器或IDE。

注意这是记事本和VS代码编辑器转码的很大的一个区别!所以尽量还是使用Notepad++这种万国码编辑器进行转码会更为安全。

程序运行后界面显示乱码

软件界面上显示的内容可能来自代码中的字符串常量、从文本获取的内容或者其他方式来源,然后再经由代码里的各种变量中转,最后显示在界面上,因此这里可能会有三种情况导致软件运行后界面显示乱码:

①数据获取时乱码

②数据传递时乱码

③数据显示时乱码

这里通过获取.ini文本,来展示以上三种情况。

测试文本:

test.ini,内容如下:
21

test_UTF16LE.ini,内容如下:
22

开发系统:Win11简体中文系统

运行系统:Win11繁体中文系统

数据获取时乱码

获取方式(Win API):GetPrivateProfileStringW

QLabel* label = new QLabel(this);
WCHAR wCH[MAX_PATH];
DWORD dwRet = ::GetPrivateProfileStringW(L"test", L"key", NULL, wCH, MAX_PATH, L"./test.ini");
QString qsShow = QString::fromWCharArray(wCH);
label->setText(qsShow);

简体系统上可正常显示:
23

繁体系统上运行乱码:
24

乱码分析:

主要是因为GetPrivateProfileStringW会根据系统的默认语言设置(也就是你的操作系统的区域设置)去解码ini文件。

在简体中文系统上时,系统的默认编码就是GB2312,因此用GetPrivateProfileStringW去读取GB2312编码的文件时不会有问题,可以正常显示。

然而,当你在繁体中文系统上时,操作系统的默认编码是Big5,这时如果用GetPrivateProfileStringW去读取GB2312编码的文件,由于编码类型的差异,就会导致显示乱码。

当Windows API尝试用Big5去解码GB2312的内容时,由于两种编码之间的字符集不匹配,就产生了乱码。

即获取.ini文本数据时,就已经因为编解码不一致导致获取到的数据就已乱码,因此不管后面如何传递、显示,都是在乱码的基础上进行的,当然也都是乱码的。

那这里使用GetPrivateProfileStringA获取GB2312编码的内容呢?

char ch[MAX_PATH];
DWORD dwRet = ::GetPrivateProfileStringA("test", "key", NULL, ch, MAX_PATH, "./test.ini");
QString qsShow = QString::fromLocal8Bit(ch);
label->setText(qsShow);

简体系统上可正常显示:
25

繁体系统上运行乱码:
26

这里乱码的原因就不是:使用GetPrivateProfileStringA获取时乱码了,而是:

QString qsShow = QString::fromLocal8Bit(ch);

这句导致乱码。文本编码为GB2312,在简体系统上QString::fromLocal8Bit,来自本地系统编码GB2312,编码GB2312-解码GB2312,编解码一致,可正常显示。

但在繁体系统上,QString::fromLocal8Bit,来自本地系统编码BIG5,即编码GB2312-解码BIG5,编解码不一致导致乱码。

这应该属于情况③数据显示时乱码 了,在这里提到是想表达尽量使用通用的编码格式(UTF-8、UTF-16),而且也要尽量避免使用受本地系统默认编码的转换接口。

因此对于这种情况:数据获取时乱码,将文本编码转换成UTF-16,使用GetPrivateProfileStringW获取,会更通用一些:

WCHAR wCH[MAX_PATH];
//GetPrivateProfileStringW直接获取UTF-16编码内容,不受本地系统编码影响
DWORD dwRet = ::GetPrivateProfileStringW(L"test", L"key", NULL, wCH, MAX_PATH, L"./test_UTF16LE.ini");
//wCH存储的是UTF-16编码的内容,避免了使用QString::fromLocal8Bit
QString qsShow = QString::fromWCharArray(wCH);
label->setText(qsShow);

简体系统上可正常显示:
27

繁体系统上也可正常显示:
28

同理,使用GetPrivateProfileStringA获取UTF-16编码的test_UTF16LE.ini文本情况同上,可自行试验,这里不再赘述。

数据传递时乱码

上面情况①获取到的是WCHAR,对应的标准类型是std::wstring,大多数项目基本都很少使用这种类型,大都是使用std::string类型变量保存数据。

因此这里就会有std::wstring 到 std::string 的转换需求,这里转换时也会有乱码的风险。

本文中转换接口均来自:字符编码转换-CSDN博客

情况②乱码示例一:

WCHAR wCH[MAX_PATH];
DWORD dwRet = ::GetPrivateProfileStringW(L"test", L"key", NULL, wCH, MAX_PATH, L"./test_UTF16LE.ini");

std::wstring wsSrc(wCH);
//转换成std::string保存、传递
//注意:这里使用CP_ACP将内容转成本地编码保存到std::string,
//在简体上可正常转换,在繁体上这里转换后的sDest就会乱码
std::string sDest = WideCharToMultiByteWithAcp(wsSrc.c_str(), CP_ACP);
//因为知道上面转成系统本地编码,因此这里使用QString::fromLocal8Bit
QString qsShow = QString::fromLocal8Bit(sDest.c_str());
label->setText(qsShow);

简体系统上可正常显示:
29

繁体系统上乱码:
30

通用的做法是,转成通用的字符编码UTF-8保存在代码变量中,后续传递的也是UTF-8编码的std::stirng

WCHAR wCH[MAX_PATH];
DWORD dwRet = ::GetPrivateProfileStringW(L"test", L"key", NULL, wCH, MAX_PATH, L"./test_UTF16LE.ini");

std::wstring wsSrc(wCH);
try
{
	//转换成std::string保存、传递
    //这里转成UTF-8后保存到sDest, 65001是UTF-8的代码页标识
	std::string sDest = WcsToMbs(wsSrc.c_str(), ".65001");
    //避免了使用QString::fromLocal8Bit
	QString qsShow = QString::fromUtf8(sDest.c_str());
	label->setText(qsShow);
}
catch (...)
{
	return;
}

简体系统上可正常显示:
31

繁体系统上也可正常显示:
32

数据显示(解码)时乱码

数据显示(解码)时乱码,这种情况基本上就是不知道数据的原编码,然后使用了和原编码不一致的方式解码导致。

WCHAR wCH[MAX_PATH];
DWORD dwRet = ::GetPrivateProfileStringW(L"test", L"key", NULL, wCH, MAX_PATH, L"./test_UTF16LE.ini");

std::wstring wsSrc(wCH);
try
{
	//转换成std::string保存、传递
	std::string sDest = WcsToMbs(wsSrc.c_str(), ".65001");
	//原数据编码为UTF-8,却以为是原编码是本地系统编码,即使用QString::fromLocal8Bit
	QString qsShow = QString::fromLocal8Bit(sDest.c_str());
	label->setText(qsShow);
}
catch (...)
{
	return;
}

简体系统上显示乱码:
33

繁体系统上显示乱码:
34

正确的做法是,解码对应编码:

WCHAR wCH[MAX_PATH];
DWORD dwRet = ::GetPrivateProfileStringW(L"test", L"key", NULL, wCH, MAX_PATH, L"./test_UTF16LE.ini");

std::wstring wsSrc(wCH);
try
{
	//转换成std::string保存、传递
	std::string sDest = WcsToMbs(wsSrc.c_str(), ".65001");
	//原数据编码为UTF-8,却以为是原编码是本地系统编码,即使用QString::fromLocal8Bit
	QString qsShow = QString::fromUtf8(sDest.c_str());
	label->setText(qsShow);
}
catch (...)
{
	return;
}

简繁系统上都可正常显示:
35

小结:

上面三种情况,要么是不清楚自己管理数据的源编码导致乱码,要么是不知要转换成何种字符编码时乱码。

不管哪种情况,只要知道数据的源编码,并正确使用相应的转码、解码方式,就一定不会有乱码问题。

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

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

相关文章

C++学习笔记(三十五):c++ 函数指针及lambda表达式

本节介绍c函数指针。在一些源码中经常能看到c函数指针,但之前一直觉着这一块比较复杂,就一直没去仔细研究,终于有时间去仔细研究这一块内容了。 c风格的函数指针 函数指针是指将一个函数赋值给一个变量的方法,可以将函数作为一个参…

Oracle篇—实例中和name相关参数的区别和作用

☘️博主介绍☘️: ✨又是一天没白过,我是奈斯,DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux,也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章,并且也会默默的点赞收藏加关注❣…

可以在微信群里使用midjourney,gpt4,gemini,文心一言4.0,且免费

免费使用gpt4和midjourney 免费使用 参考链接: https://chat.xutongbao.top/

(核心变量)全国上市公司对外开放程度+dofile+参考文献(2000-2022年)

上市公司的对外开放程度数据反映了这些公司在国际市场上的活跃度和全球化程度。这包括了它们的国际贸易参与度、跨国投资和合作、国际市场的营销和品牌推广策略,以及在不同国家和地区的业务布局。此外,这段时间内不同行业和公司的对外开放程度可能有明显…

IDEA新建SpringBoot工程时java版本只有17和21

解决方法:替换源 参考博客:https://www.kuazhi.com/post/712799571.html

VirtualBox安装linuxmint-21.2虚拟机并配置网络

VirtualBox安装linuxmint-21.2虚拟机并配置网络 适用于在VirtualBox平台上安装linuxmint-21.2虚拟机。 1. 安装准备 1.1 安装平台 Windows 11 1.2. 软件信息 软件名称软件版本安装路径Oracle VM VirtualBoxVirtualBox-7.0.12-159484D:\softwareCentOS7CentOS-7.9.2009E:\…

4点优势,昂首资本使用浮动差价不使用固定差价的原因

在交易中,很多投资者和昂首资本一样,会使用浮动点差而不使用固定点差,那是因为投资者和昂首资本一样认为,使用浮动差价交易会比使用固定价差交易更有优势。 首先在大部分交易时段,价差缩小。正如投资者和昂首资本所知…

亚马逊怎么防止店铺关联?

亚马逊(Amazon)为了确保公平竞争和防止不当行为,采取了一些措施来防止店铺关联,即通过不同的方式将多个店铺相关联,以获取不正当的竞争优势。以下是一些亚马逊防止店铺关联的主要措施: 同一经营者规定&…

自己动手写编译器:自顶向下的自动状态机

本节我们介绍编译原理中一种新的数据结构叫自顶向下的自动状态机。前面我们在做词法解析时接触了大量自动状态机,他们存在一个缺陷那就是无法对要识别的字符串进行计数,因此当我们要判断括号对是否匹配时,使用在词法解析的状态机就处理不了&a…

世界人口数据分析与探索

文章目录 世界人口数据集介绍数据集 1:世界国家统计数据:数据集 2:世界人口详细信息(2023 年):数据集 3:按年份划分的世界人口(1950-2023): 数据分析导入必要…

【金猿CIO展】步长制药信息化管理与建设中心总经理束炼:IT部门既要懂技术,也要懂业务...

‍ 束炼 本文由步长制药信息化管理与建设中心总经理束炼撰写并投递参与“数据猿年度金猿策划活动——2023大数据产业年度优秀CIO榜单及奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 随着数字化转型的浪潮席卷各行各业,中国数字经济已进入快速发展阶…

Godot之StringName解析

类描述 在Godot中,StringName是唯一字符串的内置类型。 StringName 是不可变的字符串,用于唯一名称的通用表示(也叫“字符串内嵌”)。值相同的两个 StringName 是同一个对象。进行比较时比普通 String 要快很多。 对于需要 Str…

【密码学】python密码学库pycryptodome

记录了一本几乎是10年前的书(python绝技–用python成为顶级黑客)中过时的内容 p20 UNIX口令破解机 里面提到了python标准库中自带的crypt库,经验证Python 3.12.1中并没有这个自带的库,密码学相关的库目前(2024.1.12&a…

QT周四作业

题目&#xff1a; 代码&#xff1a; widget.cpp #include "widget.h" #include "ui_widget.h" #include <QDebug> Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);ui->lineEditName->setPlac…

JAVA数组以及小练习

目录 数组的概述和静态初始化 数组的地址值和元素访问 数组的遍历 数组的动态初始化 数组练习 数组的概述和静态初始化 package 数组;public class array1 {public static void main(String[] args){//格式//静态初始化//数据类型 [] 数组名 new 数组类型[]{元素1&#xf…

[开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝

C与Python中的赋值、浅拷贝与深拷贝 1. Python中的赋值、浅拷贝、深拷贝2. C中的赋值、浅拷贝、深拷贝2.1 概念2.2 示例&#xff1a;从例子中理解1) 不可变对象的赋值、深拷贝、浅拷贝2) 可变对象的赋值、浅拷贝与深拷贝3) **可变对象深浅拷贝(外层、内层改变元素)** 写在前面&…

将WAP网站封装成App体验的全新策略

一、传统的App封装方式 传统的App封装技术通常依赖于WebView组件&#xff0c;将WAP内容嵌入到一个原生App框架中。这种方法虽然可以快速实现WAP到App的转换&#xff0c;但存在着明显的缺陷&#xff1a;首先&#xff0c;WebView的性能和用户体验都无法与原生组件相提并论&#x…

2024年1月12日:清爽无糖rio留下唇齿之间的香甜

友利奈绪的时间管理 2024年1月12日08:02:28进行java程序设计的上课准备 2024年1月12日08:02:44知道java的题目有18道 2024年1月12日08:43:07随机数去重比较 2024年1月12日08:54:03C语言题目最小公倍数 2024年1月12日08:58:37C语言题目二维数组变一维数组 2024年1月12日10…

四种无监督聚类算法说明

目录 一、K-Means无监督学习&#xff08;K-Means&#xff09;的认识-CSDN博客​​​​​​ 二、Mini-Batch K-Means -- Centroid models 三、AffinityPropagation (Hierarchical) -- Connectivity models 四、Mean Shift -- Centroid models 无监督聚类是一种机器学习技术&…