因为项目需要对一些中文名字的物品进行排序,我在C#控制台工程下面测试,直接使用系统的List.Sort()函数是可以自动进行排序的。但是把同样的代码拷贝到Unity中排序就是错乱的了。
经过一番Google之后,最终猜测应该是下面这个原因。
对于C#控制台工程代码,系统采用的字符集应该是GB2312或者GBK,我在网上找了一个GB2312的字符集对应表http://ash.jp/code/cn/gb2312tbl.htm 发现汉字的编码是按照拼音的先后顺序排序的。而Unicode的字符集中的汉字是按照笔画和偏旁部首来进行排序的。
完整的Unicode中文字符集部分下载地址:
https://download.csdn.net/download/hongkenzhao/12883276
所以猜测,Unity中之所以用List.Sort()来进行排序中文名字出现错乱的问题,主要是因为Unity采用了Unicode字符集导致的。下图是Unity中字体选择字符集的选项。
解决方案一
思路:把Unicode字符集中的汉字的码值转换成GB2312或者GBK的码值,然后再对码值进行排序。
网上有解决方案是通过把Unicode的码值和GBK的码值通过一个数组建立起来索引关系
文件的下载可以在Google里面搜索FatFs关键字,然后选择“FatFs - Generic FAT Filesystem Module”中的Previous
Release进行下载。如果下载是失败可以通过这个链接进行下载:https://download.csdn.net/download/hongkenzhao/12888729
Unicode和GBK两个码值的转换cc936.c文件内有C语言实现的函数
这是C#语言版本的实现
public static UInt32 Convert2GBK(UInt32uni_)
{
inti, n, li, hi;
UInt32gbk = 0;
if (uni_< 0x80)
{
gbk = uni_;
}
else
{
hi = uni2oem.Length/ 2 - 1;
i= 0;
li= 0;
for (n = 16; n != 0; n--)
{
i = li + (hi - li) / 2;
if (uni_ == uni2oem[i* 2])
break;
if (uni_ > uni2oem[i* 2])
{
li = i;
}
else
{
hi = i;
}
}
gbk = n != 0 ? uni2oem[i* 2 + 1] : 0;
}
return gbk;
}
现在有了码值了,怎么进行排序。我当时是根据转换的到的GBK的码值,拼接成一个数字字符串,然后根据字符串进行排序。这个方案是有问题的,在数字、字母和汉字混排的时候。最终选择放弃了这个方案,选择了方案二。
Unicode和GBK转换的参考链接:
https://blog.csdn.net/yanchao7788/article/details/53196901
方案二
思路:把汉字转换成拼音,然后按照拼音的字母顺序进行排序
把汉字转换成拼音网上有两种方案:第一种是微软官方的库文件Microsoft Visual Studio International Pac里面有一个简体中文拼音转换的库Simplified Chinese Pin-Yin Conversion
Library支持把中文转换成拼音或拼音首字母缩写。这个类库有个缺点,就是遇到多音字的时候只会按照字母排序排序取出第一个拼音作为这个字的拼音,比如“石”(shi、dan)是多音字,它会显示为dan,还有“广”(guan、an),它会显示为an,这就是网上好多人说微软的库不准,常用字转换拼音都转换错误,其实不是转换错误而是你的小学语文是体育老师教的,遇到多音字就不认识它了。具体怎么使用微软这个库可以参考下面这篇blog,里面有详细的使用说明:https://blog.csdn.net/zhupengfei/article/details/107662581
第二种方案是谷歌的NPinyin开源库文件,这个是有源码的,源码地址:https://code.google.com/archive/p/npinyin/这个库在github上面也有个比人的链接可以下载到源码:https://github.com/2881099/NPinyin
NPinyin这个库解决了多音字选择问题,通过源码可以看出它是通过建立拼音和汉字之间的索引关系来实现的。这样就可以把常用字按照常用的拼音进行匹配,手动的填充了。但是这个字库也有缺陷,里面的字太少,一些生僻字或者常用的繁体字是无法找到的。如果想手动扩充也可以,后面会介绍一下NPinyin的实现原理。但是问题是我们不知道要扩充多少个字呀,所有解决方案是优先使用NPinyin库查找字的拼音,如果查找失败,那么再去微软的PinyinConversion库中找拼音,这样就可以找到正确的拼音了。
NPinyin库的实现原理是通过PyCode.cs来定义拼音对应的汉字一维数组。然后通过汉字的对应的Unicode的码值和汉字数组长度取模,算出一个数字记做hash值,然后放到PyHash.cs文件内的二维hash数组中。当做一维的索引,那么二维的索引值是怎么来的,它是通过查找PyCode的拼音所在的索引来确定的。举个例子:阿->0x963f(十六进制码值)-> 38463(十进制)-> 38463%399(399是PyCode汉字数组长度)->159(就对应PyHash中一维的所有,二维的索引就阿的拼音“a”所在的索引0)->[159]{0}这个就是最终的转换结果。
有了正确的拼音该怎么排序,下面简单介绍一下排序规则,把汉字(或者数字,字母、汉字混合都可以)转换成拼音数组和汉字单个字符数组,然后先比较拼音数组,如果拼音数字相同,那么就比较汉字的Unicode码值,如果码值也相等,就之间比较汉字的字符长度,字符短的排在前面就可以了。通过这个排序规则就可以把汉字进行正确的排序了。下面是排序规则的排序函数代码:
public class NameStruct : IComparable
{
public string strChinese = default;
public List
public List
public NameStruct() { }
public NameStruct(string chinese_, List
{
strChinese = chinese_;
chineseList = chList_;
pinyinList = pyList_;
}
//Sort by Chinese Pinyin And Unicode Code
Value
int IComparable
{
int size1 =Mathf.Min(this.chineseList.Count, other.chineseList.Count);
int size2 =Mathf.Min(this.pinyinList.Count, other.pinyinList.Count);
for (int i = 0; i < size1; i++)
{
if (this.pinyinList[i].CompareTo(other.pinyinList[i]) == 0)
{
if (i == size1- 1)
{
for (int j = 0; j < size2; ++j)
{
if (this.chineseList[j].CompareTo(other.chineseList[j]) != 0)
{
return this.chineseList[j].CompareTo(other.chineseList[j]);
}
}
}
}
else
{
return this.pinyinList[i].CompareTo(other.pinyinList[i]);
}
}
if (this.chineseList.Count > other.chineseList.Count)
{
return1;
}
else
{
return-1;
}
}
}
完整的项目测试代码下载地址:
https://download.csdn.net/download/hongkenzhao/12895744
备注:下面是我在测试过程中用到的码值查询网站,贴出来方便大家使用。
GBK码值查询地址:
http://www.mytju.com/classCode/tools/encode_gb2312.asp
Unicode码值查询地址:
https://tool.chinaz.com/tools/unicode.aspx
ASCII码的码值查询地址:
https://tool.oschina.net/commons?type=4