iL2CPP下iOS平台的GBK编码

项目开发笔记(十五)

我以为我早已经摸透各种编码,各种开发语言转码早就可以写得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-

3 评论

  1. 。。。我没理解。C# 自带有这个转码功能库呃。Encoding.Convert 这个不能用吗?C# 库微软都是开源的了。你想抠出这单个函数都可以。
    是不是我哪儿理解错了。。

      1. 我看了下IL2CPP,放弃MonoVM走上这条奇怪的路,我觉得这是非常不明智的。
        C#的原本优势受限,C++部分又没办法让人利用,我干嘛不用纯C++的Unreal,开发个高级语言根本没那么难,编译转义为C++这种诡异的方式早有人想到,为什么没人这么干?因为这种方式代价大,还太蠢。
        我用不了Boost,又用不了C# Framework,要你一个邪门的语言又有何用?

发表评论

电子邮件地址不会被公开。 必填项已用*标注