文章目录
👉一、前言
项目中常常会遇到读写配置文件的需求,考虑到轻量化的问题,采取了读写ini配置文件的方法。
为了方便,我找来了一个可以读写ini文件的第三方插件【Advanced INI Parser】,却发现读写的过程中出现了中文乱码的情况,一开始我也以为是插件库对中文不友好的原因。于是我把ini文件的编码改为UTF-16 LE(原来是ANSI),发现可以解决读写中文乱码的问题。但是后来考虑到项目里的这个ini配置文件多平台都有使用到,如果把它改了另外的编码,其他平台读写后又将它改为原来的ANSI编码,那Untiy端又会出现读写中文乱码的情况了。本来都要打算放弃这个插件库读写ini文件的方案了,后来经过阅读INIParser的源码后,才了解了它读写ini文件的原理和为什么会出现中文乱码的问题。说明阅读源码的还是能帮助我们找到解决方法的,那解决方法就得从插件本身下手了。
👉二、中文乱码问题出现、INIParser读写原理及解决方法
1、搭建demo还原读写ini出现中文乱码场景
The First.demo场景:
功能:点击读取按钮,读取指定路径下的配置文件,显示到Text组件上;在输入框里输入一组键值数据,写入到ini文件中。
The Second.配置文件内容:
The Third.写脚本实现以上的功能逻辑:
using UnityEngine;
using UnityEngine.UI;
public class ReadWriteINI : MonoBehaviour
{
private string iniPath;//指定路径的ini文件
public Text showText;//显示ini文件的信息文本
public Button readBtn;
public InputField writeInput;
void Start()
{
iniPath = Application.dataPath + "/Data/Application.ini";
readBtn.onClick.AddListener(OnReadINI);//读写ini文件
writeInput.onEndEdit.AddListener(OnWriteINI);//结束编辑写入ini文件
}
/// <summary>
/// 读取ini文件
/// </summary>
private void OnReadINI()
{
INIParser ini = new INIParser();//声明一个解析ini文件的iniparser对象
ini.Open(iniPath);//打开ini文件
showText.text = ini.ToString();//显示ini文件的内容
ini.Close();//关闭文件读取
}
/// <summary>
/// 写入ini文件
/// </summary>
/// <param name="value"></param>
private void OnWriteINI(string value)
{
if (!string.IsNullOrEmpty(value))
{
INIParser ini = new INIParser();
ini.Open(iniPath);
string[] strs = value.Split('=');//输入的字符串以=分割,缓存到字符串数组
if (strs.Length>1)
{
ini.WriteValue("Unity", strs[0], strs[1]);//将数据写入到 Unity节点下
}
ini.Close();
}
}
}
The Fourth. 读取ini文件运行结果:
读取出现中文乱码,但源文件没有乱码;
The Fifth.写入ini文件运行结果:
写入那组中文数据没有乱码,但是源文件内容出现乱码。
刚开始觉得有点奇怪,不是只写入了一组键值而已吗,为什么原来的内容会出现乱码呢?
这是为什么呢?让我们阅读INIParser源码一探究竟。
2、INIParser读写ini文件原理及乱码问题说明
仔细阅读源码就不难发现:INIParser对象打开ini文件读取时使用了I/O数据流技术File.ReadAllText(). 一次性先将ini文件内容全部读取到内存中缓存到m_iniString中。File.ReadAllText()如果没有指定编码格式读取,就默认是UTF-8编码读取,我的ini文件是ANSI编码格式的,如果使用UTF-8读取肯定是会出现中文乱码的;同理INIParser对象写入ini文件时,其实是先修改了内存中的m_iniString字符串,然后再使用==File.WriteAllText(). 一次性将修改后的m_iniString内容写入到ini文件。由于它也没有指定编码格式,所以默认以UTF-8编码写入ini文件,并将ini文件改为UTF-8编码格式的文件,也就造成了原内容中文乱码,写入那部分内容正常显示中文。了解原理之后我就很快找到了解决方法。
3、解决方法
其实File.ReadAllText()和File.WriteAllText()还有另一个重载方法是可以指定编码格式进行读写文件的。所以我们只需要将INIParser脚本里读写ini文件的方法指定为所需编码格式进行读写,就不会出现中文乱码的问题了。
由于我的配置文件默认就是ANSI格式了,所以我需要将读写ini文件都改为ANSI格式。而Encoding.Default 就是我项目当前.NET平台默认的ANSI格式,所以将其作为形参传入到File.ReadAllText()和File.WriteAllText()方法里。
修改后运行的读写ini结果:
可以看到读取时使用ANSI编码不会出现乱码,写入后ini文件还是原来的ANSI编码,所以也不会出现中文乱码的情况了,至此Unity使用INIParser读写ini配置文件出现中文乱码问题得以解决。
4、友情提示:谨慎使用Encoding.Default !!!
可能大家看到我这里使用Encoding.Default用来处理ANSI编码的文件,就以为Encoding.Default就是表示ANSI的,其实不是的。是因为我知道我当前项目的.net平台默认代码页的编码是ANSI,所以使用Encoding.Default来指定读写ini文件。在你不了解你当前.net平台默认编码时,为了避免使用Encoding.Default出现乱码,尽量谨慎使用Encoding.Default!!!因为这与你当前Unity版本选择的脚本运行.NET平台有关:(可以在PlayerSetting/OhterSetting查看,不同的计算机可以使用不同的编码作为默认编码)
本人实践发现:.net2.0和.net3.5平台下Encoding.Default均表示ANSI编码,.net4.x表示UTF-8编码
所以建议大家在读写文件时使用指定编码格式的方法。
比如指定ANSI编码可以这么写:
Encoding winLatinCodePage = Encoding.GetEncoding("gb2312");//传入字符串GBK也可以
m_iniString = File.ReadAllText(m_FileName, winLatinCodePage);//winLatinCodePage就表示ANSI编码
大家可以去了解一点C#中Encoding类编码的知识
👉三、学点额外的知识点
1、INIParser插件的简单使用说明
使用流程:
INIParser ini = new INIParser();//1.声明INIParser对象
ini.Open(iniPath);//2.打开指定路径的ini文件,进行读取操作,如果没有会创建新文件
ini.ReadValue(...);ini.WriteValue(...);//3.调用ini读写的api,进行读写操作。
ini.Close();//4.读写完成之后关闭文件读写操作,保存文件
常用API:
public string ReadValue(string SectionName, string Key, string DefaultValue)
从本地缓存读取值,读取SectionName(指定节点)下的key(指定键)的值,如果没有则返回DefaultValue(默认值)。
public void WriteValue(string SectionName, string Key, string Value)
在本地缓存中插入或修改一个值,写入SectionName(指定节点)下的key(指定键)和value(指定值),如果存在该节点和键值,则修改;否则是添加该节点和键值到文件中。
其实这个插件就一个脚本,如果有读写ini文件的话推荐大家使用,源码看上去也比较容易理解,插件也带有使用文档,大家可以去研究学习一下。
2、Unity中使用Encoding类的学习
1.Encoding类位于System.Text命名空间中,下面两个表格是Encoding类的常用属性和方法:
属性 | 说明 |
---|---|
Default | 获取当前代码页.NET 平台实现的默认编码。 |
Unicode | 表示使用Little-Endian字节顺序的UTF-16格式的编码 |
UTF-8 | 表示UTF-8格式的编码 |
UTF-7表示UTF-7格式的编码 | |
ASCII | 表示ASCII(7位)字符集的编码 |
UTF32 | 表示使用 Little-Endian 字节顺序的 UTF-32 格式的编码 |
BigEndianUnicode | 表示使用 Big Endian 字节顺序的 UTF-16 格式的编码。 |
方法 | 说明 |
---|---|
Convert | 将字节数组从一种编码转换为另一种编码 |
GetBytes | 将一组字符编码为一个字节数组 |
GetString | 将一个字节数组解码为一个字符串 |
GetEncoder | 获取一个编码器,该编码器将Unicode字符序列转为已编码的字节序列 |
GetDecoder | 获取一个解码器,该解码器将已编码的字节序列转为字符序列 |
GetEncoding | 返回指定格式的编码 |
2.利用Encoding类的Convert方法可将字节数组从一种编码转换为另一种编码。方法原型为:
Public static byte[]Convert(Encoding srcEncoding,Encoding dstEncoding,byte[]bytes)
各参数含义如下。
srcEncoding:表示源编码格式。
dstEncoding:表示目标编码格式。
Bytes:待转换的字节数组。
返回值为包含转换结果的Byte类型的数组。
3.调用以下方法可以指定编码字符串转为目标字符串
/// <summary>
/// 将返回指定编码的字符串转为目标编码的字符串
/// </summary>
/// <param name="src">源编码</param>
/// <param name="dst">目标编码</param>
/// <param name="text">传入的字符串</param>
/// <returns></returns>
public static string EncodingConvert(Encoding src, Encoding dst, string text)
{
var bytes = src.GetBytes(text);//获取指定编码的字符串转换的字节数组
bytes = Encoding.Convert(src, dst, bytes);//将源编码字节数组转为目标编码字节数组
return dst.GetString(bytes);//返回目标编码字节数组转换的字符串
}
👉四、插件的获取方法
1.直接打开 Window/Asset Store 搜【INIParser】,插件是开源免费的,直接下载导入即可。
3.可以留言或私信我发给你😀