项目开发笔记(十五)
我以为我早已经摸透各种编码,各种开发语言转码早就可以写得666,但是这次填坑真的把人虐得欲罢不能.这次的事情有比较苛刻的前提条件,所以不一定适用于通过搜索找到此文章的各位遇到的类似情况.
先说说项目基本情况:
Client: Unity 5.3.4 (C#开发逻辑,Java OC原生开发支持,iOS下iL2CPP编译模式) Server: 纯C++开发的服务器,PHP辅助
0x01. 引发的GBK编码问题
这个项目的网络数据的全部中文都是走GBK格式,数据到了客户端在iOS下所有中文均显示乱码.在 PC和Android下正常
关于编码问题有一篇经典文章 – 字符编码笔记:ASCII,Unicode和UTF-8
这个项目是一个超过十年的老项目(我主要负责移动客户端的实现),服务端使用纯c++开发,服务端底层架构和所有模块已经正常运行十年以上.导致遇到很多问题只能是客户端去适应服务端.所以不可能让协调服务端直接修改数据格式.
0x02. 使用I18N.CJK.DLL库
一个被提及最多的GBK格式数据在iOS iL2CPP模式下的转换方案是: 在link.xml中添加配置防止I18N.CJK.DLL被裁剪.配置方式为:
需从Unity安装目录 Editor\Data\Mono\lib\mono\unity 或 Editor\Data\Mono\lib\mono\2.0 两个目录中,拷贝I18N.DLL 和I18N.CJK.DLL到项目目录Asset文件夹下,在Assets根目录下添加link.xml文件,其内容如下:
但是我反复测试,这种方式在我当前的环境下(Unity 5.3.4,iOS,iL2CPP,Mac Pro,iPhone7P)一直是失败的.
0x03. 使用objective-c实现转码
使用C#各种库走不通,那只能直接使用原生支持了.Unity的 iL2CPP这个东西真的非常不成熟,各种库各种类动不动就被裁剪.这都多少年过去了…在去年我就把对iL2CPP纳入我面试Unity应聘者的技术点之一了,可惜问来问去,没几个真正懂的,遇到问题,即使解决了也没多少人能给出很合理的解释.话说回来 使用objective-c实现GBK转码非常简单,方法如下:
extern "C" const char* MFWGB2312toUTF8(char* pstr,int nLen) { NSString *pStr = [[NSString alloc] initWithFormat:@"%s" ,pstr]; if (0 >= nLen) { char* res = (char*)malloc(strlen(pstr)+1); strcpy(res, pstr);//必须copy一份 避免il2cpp中自动释放内存错误 return res; } NSStringEncoding encodingGBK = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); NSString* hexString = [pStr stringByReplacingOccurrencesOfString:@"-" withString:@""]; int j = 0; Byte bytes[2 * nLen]; for(int i=0;i< [hexString length];i++) { int int_ch; unichar hex_char1 = [hexString characterAtIndex:i]; int int_ch1; if(hex_char1 >= '0' && hex_char1 < ='9') int_ch1 = (hex_char1-48)*16; else if(hex_char1 >= 'A' && hex_char1 < ='F') int_ch1 = (hex_char1-55)*16; else int_ch1 = (hex_char1-87)*16; i++; unichar hex_char2 = [hexString characterAtIndex:i]; int int_ch2; if(hex_char2 >= '0' && hex_char2 < ='9') int_ch2 = (hex_char2-48); else if(hex_char1 >= 'A' && hex_char1 < ='F') int_ch2 = hex_char2-55; else int_ch2 = hex_char2-87; int_ch = int_ch1+int_ch2; bytes[j] = int_ch; j++; } NSData *data = [[NSData alloc] initWithBytes:bytes length:nLen]; NSString* GBKString = [[NSString alloc] initWithData:data encoding:encodingGBK]; if (nil == GBKString || [GBKString isEqualToString:nil]) { char* res = (char*)malloc(strlen(pstr)+1); strcpy(res, pstr); return res; } const char* resNoCopy = [GBKString UTF8String]; char* res = (char*)malloc(strlen(resNoCopy)+1); strcpy(res, resNoCopy); return res; }
在脚本C#定义一下:
//------------------------------------------------------------------------- [DllImport("__Internal")] public static extern string MFWGB2312toUTF8(string strValue, int nByteCount); //-------------------------------------------------------------------------
如上即可解决问题.
完整的代码 请移步 Unity的GBK转码器仓库地址 – https://github.com/recter/UnityGBKParser
-EOF-
。。。我没理解。C# 自带有这个转码功能库呃。Encoding.Convert 这个不能用吗?C# 库微软都是开源的了。你想抠出这单个函数都可以。
是不是我哪儿理解错了。。
因为在 il2CPP 模式下 他们被剥离了,从脚本变成二进制字节码,或者用 “C++脚本” 比喻更合适,总之就是被弄得奇怪.有种C#直接转换成C++的感觉.
more 1 – https://forum.unity.com/threads/asset-bundles-il2cpp-code-stripping.367239/
more 2 – https://blogs.unity3d.com/cn/2015/05/13/il2cpp-internals-a-tour-of-generated-code/
我看了下IL2CPP,放弃MonoVM走上这条奇怪的路,我觉得这是非常不明智的。
C#的原本优势受限,C++部分又没办法让人利用,我干嘛不用纯C++的Unreal,开发个高级语言根本没那么难,编译转义为C++这种诡异的方式早有人想到,为什么没人这么干?因为这种方式代价大,还太蠢。
我用不了Boost,又用不了C# Framework,要你一个邪门的语言又有何用?