关于Lua处理GBK时编码出错的原因分析以及解决方案
关于Lua处理GBK时编码出错的原因分析以及解决方案
以下这段lua在windows下会乱码,在linux下则会出错导致后续加载异常,导致大量Npc对话文本丢失
tEditorStrings_21 = {}
tEditorStrings_21[9672] = "闬闳高轩"
Linux下的报错
invalid escape sequence near '"?
出错原因是因为“闬”这个字在二进制下后半段是ASCII码的\
二进制:
GBK对应编码“闬”= EA 5C
而ASIIC编码下的5C,也就是“闬”的后半个字节,其实是斜杠
lua的底层代码处理这部分的时候有一些问题
jit的版本:
遇到这种情况直接报错跳转了
而原生lua,虽然做法不同,但是也有问题
虽然没有抛错跳转,但是之前已经做了next,所以丢弃了一个/
会导致后续字符全部错乱,显示乱码
影响范围:
GBK所有以5C结尾的字符都会受这个bug影响
不算自定义区段,受影响的有119个字符
乗俓僜刓匼哱嘰圽塡奬
媆孿峔嶾廫怽慭抃揬擻
昞朶梊榎橽歕沑淺漒瀄
焅燶猏玕琝璡甛痋癨盶
瞈砛碶礬禱穃竆筡篭籠
糪絓綷縗繺羂耚肻腬臷
芢荺萛蒤蔦薥蘚蚛蝄蟎
衆裓襖覾診誠謀譢豛賊
赲踈躙輁轡運郳醆鈂鉢
鋅錦鎈鏫鑌閈闬隲靄韁
頫颸餦馶騖骪鬨鮘鯸鱘
鳿鵟鶿鸤黒齖╘‐
总共119个
其中有一些也不算生僻字,如果一起禁止会限制策划的文案表达
但是有个山寨解决办法,可以在出错字符后面补一个斜杠
"闬\闳高轩"
但这个非常违反直觉,因为是加在出错字符后面的, 目前端游是用这个方法解决了部分显示异常
如果用代码修复的话,这部分脚本需要重新处理
正规的办法:
可以在处理前一个字节的时候检查,如果是落在GBK第一字节范围内的字符,直接跳过后一个字节
static void read_string (LexState *ls, int del, SemInfo *seminfo) {
save_and_next(ls);
while (ls->current != del) {
if (ls->current & 0x80 && ls->current != EOZ) // 兼容GBK编码
{
save_and_next(ls);
save_and_next(ls);
continue;
}
switch (ls->current) {
case EOZ:
luaX_lexerror(ls, "unfinished string", TK_EOS);
continue; /* to avoid warnings */
case '\n':
case '\r':
luaX_lexerror(ls, "unfinished string", TK_STRING);
continue; /* to avoid warnings */
case '\\': {
int c;
这种改法经过验证是可以解决GBK问题的,但是对UTF8又不兼容
UTF8的编码规则:
UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。
如表:
1字节 0xxxxxxx
2字节 110xxxxx 10xxxxxx
3字节 1110xxxx 10xxxxxx 10xxxxxx
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
可以看出utf8的编码任何字节与ASCII没有任何交集,天然就不会出之前的问题
但是如果加上了之前的GBK补丁代码,可能就会出现不兼容,因为可能错误的“吃掉”ASCII字符,如果被吃掉的是个"/"就会导致后续转义失败
最笨的办法是从第一个字节开始数1的个数,然后往后“窥”10开头的字符有多少个,但是也是有可能误判把GBK误当做utf处理的,不是很安全
另一种可行的办法就是在开始执行GBK兼容前先判断一下当前系统编码没有使用utf-8,这个暂时没有找到合适的API
不过目前游戏内应该没有存在混用GBK与utf8的问题,除了海外版,没有别的版本在用utf8
那么可以编译两个不同版本的lua.a,一个针对GBK打上补丁,另一个utf8的则不用做任何修改
Engine在不同的版本选择性链接, 或者在lua源码上增加一个开关,由Engine默认打开,在海外版默认关闭
当然,如果所有的脚本都转换成utf8就天然没这个问题了
从展鸿这里了解到,客户端现在会从gbk的脚本加载,然后在内存里转换成utf8 (在lua加载完以后,UI C++内存的是utf8, 但这个其实已经晚了,gbk的问题字符在加载过程中已经坏了)
老孙的脚本编辑器内部处理也是utf8,导出才转了gbk ...
UTF8 是Unicode的一种节省空间的编码形式,可以和Unicode区间一一对应,只是为了识别起来方便加了前缀,去除这些前缀映射的区间是连续的
但是GBK和Unicode没有连续区间对应关系,需要有一个映射表进行转换,这个映射表网上有,可以找到
其实最好的办法就是在lua上自己实现一个接口,设置是否GBK编码,在明确知道处理的是GBK编码以后做对应处理是最好的
本文来自博客园,作者:叶二少,转载请注明原文链接:https://www.cnblogs.com/yefanlab/p/17858114.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)