乱码问题汇总

news2024/11/18 17:42:12

写在前面

在工作中经常会碰到各种莫名其妙的乱码问题,但通过之前的学习:字符集&字符编码-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

小结:

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

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

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

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

相关文章

面试算法118:多余的边

题目 树可以看成无环的无向图。在一个包含n个节点(节点标号为从1到n)的树中添加一条边连接任意两个节点,这棵树就会变成一个有环的图。给定一个在树中添加了一条边的图,请找出这条多余的边(用这条边连接的两个节点表示…

Unity中URP下实现深度贴花

文章目录 前言一、场景设置二、实现思路1、通过深度图求出像素所在视图空间的Z值2、通过模型面片的求出像素在观察空间下的坐标值3、结合两者求出 深度图中像素的 XYZ值4、再将此坐标转换到模型的本地空间,把XY作为UV来进行纹理采样 三、URP下实现1、通过深度图求出…

2024年最新软件测试面试题

Part1 1、你的测试职业发展是什么?【文末有面试文档免费领取】 测试经验越多,测试能力越高。所以我的职业发展是需要时间积累的,一步步向着高级测试工程师奔去。而且我也有初步的职业规划,前3年积累测试经验,按如何做…

Spark的内核调度

目录 概述 RDD的依赖 DAG和Stage DAG执行流程图形成和Stage划分 Stage内部流程 Spark Shuffle Spark中shuffle的发展历程 优化前的Hash shuffle 经过优化后的Hash shuffle Sort shuffle Sort shuffle的普通机制 Job调度流程 Spark RDD并行度 概述 Spark内核调度任务: 1…

力扣67. 二进制求和算法

一、【写在前面】 这道题需要,给你两个字符串比如 a "1010", b "1011"答案是:"10101" 然后需要你给出计算结果,那么我们很容易想到两种做法 1. 调库做法:直接转化为整数,然后用内…

python统计分析——小提琴图(sns.violinplot)

参考资料:用python动手学统计学,帮助文档 使用seaborn.violinplot()函数绘制箱线图 sns.violinplot()的做出来的小提琴图比plt.violinplot()更像小提琴。 import numpy as np import pandas as pd from matplotlib import pyplot as plt import seabo…

【自控实验】4. 数字仿真实验

本科课程实验报告,有太多公式和图片了,干脆直接转成图片了 仅分享和记录,不保证全对 使用matlab中的simulink进行仿真 实验内容 线性连续控制系统的数字仿真 根据开环传递函数G(S)的不同,完成两个线性连续控制系统的仿真。 …

MySQL面试题 | 03.精选MySQL面试题

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…

【linux】NIO中的FileChannel与mmap

FileChannel是Java NIO库中的一个类,用于对文件进行读写操作。它提供了一种高效的方式来读取、写入和操作文件。 使用FileChannel,你可以执行以下操作: 从文件读取数据到缓冲区(Buffer):你可以使用FileCh…

QT 小组件 列表框以及微调框

.cpp文件 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);QListWidgetItem *pPhone new QListWidgetItem;pPhone->setText("西瓜");pPhone->…

SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测

SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测 目录 SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多…

eclipse ADT安装及abap cds模版创建

文章目录 1.前提2.安装3.创建cds模版 abap cds 常用语法 https://blog.csdn.net/weixin_49198221/article/details/135531478?spm1001.2014.3001.5501 1.前提 需要了解版本关系: **1.eclipse:**2023-06 (4.28), 2023-09 (4.29), 2023-12 (4.30) 2.Windows: ​ 1.Windows …

java多线程(并发)夯实之路-线程池深入浅出

线程池 Thread Pool:线程池,存放可以重复使用的线程(消费者) Blocking Queue:阻塞队列,存放等待执行的任务(生产者) poll方法(有时限地获取任务)相对take注…

【每日小bug】——mybatis-plus拼接sql空格报错,根据时间聚合查询

mybatis-plus拼接sql报错 复制报错sql语句到navicat,字段之间缺少空格,补上就可以了 聚合sql 根据时间 json接收JsonFormat(timezone "GMT8", pattern "yyyy-MM-dd")DateTimeFormat(pattern "yyyy-MM-dd")private Date startTim…

如何为数据保护加上“安全锁”?

伴随着数字经济的日趋活跃,数据安全和隐私保护成为了各国政府和企业都十分重视的问题,纷纷加强了数据安全防护。但实际上,近几年数据泄露问题接连不断,虽然没有造成严重的后果,但也足以证明目前数据安全防护的紧迫性。…

使用 Github、Hugo 搭建个人博客

Hugo 静态网站构建手册:https://jimmysong.io/hugo-handbook/ 关键字:开源 博客 框架 1、GitHub Pages 官网:https://pages.github.com/ 文档:https://docs.github.com/zh Github Pages 简介 Websites for you and your project…

京东年度数据报告-2023全年度游戏本十大热门品牌销量(销额)榜单

同笔记本市场类似,2023年度游戏本市场的整体销售也呈下滑态势。根据鲸参谋电商数据分析平台的相关数据显示,京东平台上游戏本的年度销量累计超过350万,同比下滑约6%;销售额将近270亿,同比下滑约11%。 鲸参谋综合了京东…

2.idea查看不到git的提交文件

(1)查看日志 使用idea工具查看git提交日志,如下:项目名上右击,选择git->Show History (2)预期结果 (3)实际结果 只能看见此次提交的commit id,看不见所修…

leetcode 动态规划(爬楼梯、零钱兑换、完全平方数)

70. 爬楼梯&#xff08;进阶版&#xff09; 卡码网&#xff1a;57. 爬楼梯(opens new window) 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬至多m (1 < m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 注意&#xff1a;给定 n 是一个正…

用单片机设计PLC电路图

自记&#xff1a; 见另一篇文章&#xff0c;MOS驱动差了一个充电电容&#xff0c;栅极电容充电会有问题&#xff1b; 光耦用的直插&#xff0c;但板子用的贴片&#xff0c;此文档仅供参考 基本列出了PCB板情况&#xff0c;基础元器件&#xff0c;部分连接&#xff0c;原理等…