乱码问题汇总

写在前面

在工作中经常会碰到各种莫名其妙的乱码问题,但通过之前的学习:字符集&字符编码-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::fromLocal8BitQString 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::fromLocal8BitQString 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::fromLocal8BitQString qsShow = QString::fromUtf8(sDest.c_str());label->setText(qsShow);
}
catch (...)
{return;
}

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

小结:

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[软件工具]pdf多区域OCR识别导出excel工具使用教程

首先我们打开软件,界面如下: 如上图,使用非常简单,步骤如下: (1)选择工具-取模板选择一个pdf文件划定自己需要识别的区域,如果你选择第2页指定区域则软件统一识别所有pdf第2页指定区…

VUE指令(一)

vue会根据不同的指令,针对不同的标签实现不同的功能。指令是带有 v- 前缀的特殊标签属性。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。 1、v-text:设置元素的文本内容,不会解…

声纳技术24.1.12信号模糊函数-多普勒频移

声纳接收机常采用方法:匹配滤波处理 由于信号的模糊函数与匹配滤波器存在一定联系,因而通过分析信号模糊函数了解声纳系统性能 一、 信号的多普勒频移 多普勒频移:声纳与目标间相对运动使接收信号波形发生改变,表现为信号频率偏…

【LeetCode】206. 反转链表(简单)——代码随想录算法训练营Day01

题目链接:206. 反转链表 题目描述 206. 反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1] 示例 2: 输入&#xff1…

fastadmin 框架如何移除图片上传后预览中的删除按钮

在FastAdmin中,当我们启用了图片上传预览时,在预览区域会自动生成预览图和删除按钮,如下图: 如果我们想上移除掉这里的删除按钮,则需要启用自定义预览模板的功能。 首先我们找到视图中我们的预览容器,比如…

【排序算法】插入排序与希尔排序,你不想知道为什么希尔比插入更快吗?

文章目录 🚀前言🚀插入排序(insertsort)✈️原理✈️代码实现(coding) 🚀总结🚀希尔排序(shellsort)✈️代码实现(coding)✈️为啥希尔…

Fast.ai

网址:https://colab.research.google.com/ https://colab.research.google.com/notebooks/gpu.ipynb#scrollTotMce8muBqXQP 克隆此存储库后,请确保您已 nbdev_install_hooks在终端中运行。这将安装 Jupyter 和 git hooks 以自动清理、信任和修复笔记本中…

Angular - 笔记

文章目录 语法属性绑定引用模板变量组件绑定父组件传子组件 input子组件传父组件 outputEventEmitter ViewChildViewChildren获取子组件对象列表 管道常用模块 函数localStorage实现数据持久化简介使用 参考文档 语法 属性绑定 Angular 的双向绑定语法是方括号和圆括号的组合 …

Unity填坑-灯光烘焙相关

Unity填坑-灯光烘焙相关 文章目录 Unity填坑-灯光烘焙相关前言一、Light的模式二、光的效果分类三、各种Light模式与烘焙的说明1.Realtime,实时光2.baked,烘焙光3.mixed,混合 四、实时全局光五、其他说明1.动态物体的全局光照效果2.手机使用烘焙注意的点3.其他设置 前言 项目组…

GB28181/GB35114平台LiveGBS何如添加白名单,使指定海康、大华、华为等GB28181摄像头或录像机设备可以免密接入

1、什么是GB/T28181级联 协议定义中的解释如下: 级联 cascadednetworking 两个信令安全路由网关之间按照上下级关系连接,上级中心信令控制服务器通过信令安全路由网 关可调用下级中心信令控制服务器所管辖的监控资源,下级中心信令控制服务器通过信令安全路由网 关向…

(26)Linux 进程通信之共享内存(共享储存空间)

共享内存是System V版本的最后一个进程间通信方式。共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一…

端侧AI的“春风化雨手”,翻开中国科技下一页

大模型是一年多来全球科技圈的最大热点,手机厂商想要借助大模型的锋芒,打造高端形象,获得新的增长,这无可厚非。 不过,大家注意到没有,越是“AI强者”,对待大模型越举重若轻。 简单来说&#xf…

43-函数的声明定义,函数表达式定义,函数的调用,声明提升,参数,形参,实参

1.函数声明定义 function 函数名(){} 2.函数表达式定义 匿名式函数表达式 var 函数名 function(){} 命名式函数表达式 var 函数名 function 函数关键字(){} 3.函数的调用 var fn function f(){alert("aaa");}//函数名()//函数想调用几次…

rpb/rpc文件说明与matlab读取

什么是rpb/rpc文件? rpb文件是用来存储用于遥感数据几何校正的RPC(Rational Polynomial Coefficients )模型的文件。类似的还有RPC文件,rpb与rpc文件只是格式不同,但包含的信息一致。其用于从图像坐标转换到地理坐标&a…

acwing4986.互质数的个数

题目不难 有个好的细节想着分享一下 一开始写的有点问题&#xff5e;需要特判掉一个... #include<bits/stdc.h> using namespace std; using ll long long; const int N 1e510;const ll mod 998244353;ll qmi(ll a,ll b){ll ans 1;while(b){if(b&1)ans ans*a%…

浅谈6种流行的API架构风格

前言 API在现代软件开发中扮演着重要的角色&#xff0c;它们是不同应用程序之间的桥梁。编写业务API是日常开发工作中最常见的一部分&#xff0c;选择合适的API框架对项目的成功起到了至关重要的作用。本篇文章将浅谈一下当前6种流行的API架构风格的优点、缺点以及适用场景。 …

反向传播(Back Propagation)

目录 回归简单模型的梯度计算 反向传播计算图链式求导链式法则定理&#xff1a; Forward 前馈计算反向传播Back Propagation例子线性模型的计算图计算前馈过程反向传播过程&#xff08;逆向求导&#xff09; 练习 Pytorch中的前馈过程和反向传播过程Tensor代码小结 回归 简单模…

中药房数字化-亿发中药饮片信息化建设方案,中药材饮片智能追溯

中药&#xff08;包括中成药、颗粒剂、中药饮片等&#xff09;是中医临床的重要工具和武器&#xff0c;中药材是其中的核心要素。在这一体系中&#xff0c;“药材好&#xff0c;药才好”是关键&#xff0c;因为只有中药材的品质稳定和高效&#xff0c;才能最大限度地确保中医治…

Android音视频: 引入FFmpeg

本文你可以了解到 本文将介绍如何将上一篇文章编译出来的 FFmpeg so 库&#xff0c;引入到 Android 工程中&#xff0c;并验证 so 是否可以正常使用。 一、开启 Android 原生 C/C 支持 在过去&#xff0c;通常使用 makefile 的方式在项目中引入 C/C 代码支持&#xff0c;随…

Minitab的单因子方差分析的结果

单因子方差分析概述 当有一个类别因子和一个连续响应并且想要确定两个或多个组的总体均值是否存在差异时&#xff0c;可使用 单因子方差分析。如果经检验&#xff0c;发现至少有一组存在差异&#xff0c;请使用单因子方差分析中的比较对话框来标识存在显著差异的组对。 例如&…