JS中使用的是UTF-16编码,因为历史原因,中心端接收的数据是GBK编码的,因此在数据发送和接收时需要进行转换操作。转换的原理是将GBK编码范围内的字符全部提取出来,并获取其对应的GBK编码和UTF-16编码,生成两张对照表。因为两者都是双字节编码(UTF-16有少量不常用汉字采用4字节编码,因为没有找到这部分数据,先不做处理),所以同一个字符在两个表的位置是一样的,这样就可以获取到对应的编码了。
编码数据的获取是通过Android程序来实现
获取全部GBK编码范围内的字符文本(CharsetUtils.java)
/**
* 获取一段GBK编码范围内的字符串, 总体编码范围为 8140-FEFE
*
* @param start
* 编码起始值, 大于等于0x8140
* @param end
* 编码结束值, 小于等于0xFEFE
* @return
*/
public static String getGBKTable(int start, int end) {
StringBuilder sb = new StringBuilder(10000);
if (start < 0x8140) {
start = 0x8140;
}
if (end < start || end > 0xFEFF) {
end = 0xFEFF;
}
int highStart = (start >> 8) & 0xff;
int lowStart = start & 0xff;
int highEnd = (end >> 8) & 0xff;
int lowEnd = end & 0xff;
byte[] data = new byte[2];
try {
for (int high = highStart; high <= highEnd; high++) {
for (int low = lowStart; low < lowEnd; low++) {
// 跳过0xXX7F这个空白的码位
if (low == 0x7F) {
continue;
}
// 跳过空白码位, 用户自定义区: F8A1-FEFE
if (high >= 0xF8 && low > 0xA0) {
continue;
}
// 跳过用户自定义区: A140-A7A0
if (high >= 0xA1 && high <= 0xA7) {
if (low <= 0xA0) {
continue;
}
}
data[0] = (byte) (high);
data[1] = (byte) (low);
sb.append(new String(data, "GBK"));
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return sb.toString();
}
获取文本的GBK编码,将编码转为4位十六进制数(如C4A1),编码间以逗号隔开。UTF-16编码也是类似处理,只是有分大小端两种模式,实际测试中在Android平台上是大端(0xFFFE打头),JS平台下是小端的,因此需要做一个高低位掉换。将获取的编码都保存到文件以便于导入到JS代码中。
private void getTotalTable() {
String totalStr = CharsetUtils.getGBKTable(0x8140, 0xFEFF);
getGbkCodeFile(totalStr);
getUtfCodeFile(totalStr);
FileManager.writeFile(GbkTableFile, totalStr);
// 显示字符串的片段
EditText edit = (EditText) findViewById(R.id.edit_hanzi);
edit.setText(totalStr.substring(0, 100));
Toast.makeText(GBKToUTFActivity.this, "合计输出GBK字符 " + totalStr.length(), Toast.LENGTH_SHORT).show();
}
private void getGbkCodeFile(String input) {
try {
StringBuffer sb = new StringBuffer(input.length() * 4);
byte[] gbkArray = input.getBytes("GBK");
for (int i = 0; i < gbkArray.length; i++) {
int a = byteToInt(gbkArray[i]);
int b = byteToInt(gbkArray[++i]);
sb.append(String.format("%04x,", a * 256 + b));
}
String fileName = Constant.CRM_DIR + "/gbk_code.txt";
FileManager.writeFile(fileName, sb.substring(0, sb.length() - 1).toUpperCase());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private void getUtfCodeFile(String input) {
try {
StringBuffer sb = new StringBuffer(input.length() * 4);
byte[] utfArray = input.getBytes("UTF-16");
// 判断当前平台是大端还是小端
// boolean isBigEndian = false;
// if (utfArray[0] == -1 && utfArray[1] == -2) {
// isBigEndian = true;
// }
for (int i = 2; i < utfArray.length; i++) {
int a = byteToInt(utfArray[i]);
int b = byteToInt(utfArray[++i]);
sb.append(String.format("%04x,", b * 256 + a));
}
String fileName = Constant.CRM_DIR + "/utf_code.txt";
FileManager.writeFile(fileName, sb.substring(0, sb.length() - 1).toUpperCase());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 将字节转化为整型
*
* @param a
* @return 整数
*/
private int byteToInt(byte a) {
return (a & 0xff);
}
JS端的转换接口
UTF-16编码数据片段如下格式,从前述接口获取到的"utf_code.txt"文件中获得
UnicodeCharTable =
'4E02,4E04,4E05,4E06,4E0F,4E12,4E17,4E1F,4E20,4E21,4E23,4E26,4E29,4E2E,4E2F,4E31,4E33,4E35,4E37,4E3C,4E40,4E41,
...
E862,E863,E864';
GBK编码数据片段如下格式,从前述接口获取到的"gbk_code.txt"文件中获得
GBKCharTable =
'8140,8141,8142,8143,8144,8145,8146,8147,8148,8149,814A,814B,814C,814D,814E,814F,8150,8151,8152,8153,8154,8155,
...
FE9B,FE9C,FE9D,FE9E,FE9F,FEA0';
GBK字节流转字符串调用:UTF16StrToGBKArray,字符串转GBK字节流调用:GBKArrayToUTF16Str
/**
* 将unicode通过查表转换,转为gbk的code
* @param {Number} chrCode 字符unicode编码
*/
UniCode2GBKCode(chrCode) {
let chrHex = chrCode.toString(16).toUpperCase();
let i = this.UnicodeCharTable.indexOf(chrHex);
if (i != -1) {
chrHex = this.GBKCharTable.substr(i, 4);
}
return parseInt(chrHex, 16)
};
/**
* 将gbk的对应的code通过查表转换,转为unicode
* @param {Number} chrCode gbk字符对应的编码
*/
GBKCode2UniCode(chrCode) {
//以16进制形式输出字符串
let chrHex = chrCode.toString(16).toUpperCase();
let i = this.GBKCharTable.indexOf(chrHex);
if (i != -1) {
chrHex = this.UnicodeCharTable.substr(i, 4);
}
return parseInt(chrHex, 16)
};
/**
* 将utf16编码的字符串(js内置编码)转为GBK编码的字节流数组
* @param {String} str utf16编码的字符串(js内置)
* @return {Array} 转换后gbk编码的字节流数组
*/
UTF16StrToGBKArray(str) {
let res = [];
if (!str) {
return res;
}
let len = str.length;
for (let i = 0; i < len; i++) {
//获得对应的unicode码
let code = str.charCodeAt(i);
if (code < 0) {
code += 65536;
}
if (code > 127) {
code = this.UniCode2GBKCode(code);
}
if (code > 255) {
let varlow = code & 65280;
varlow = varlow >> 8;
//取得高位
let varhigh = code & 255;
res.push(varlow);
res.push(varhigh);
} else {
res.push(code);
}
}
return res;
};
/**
* 将GBK编码的文本数组转为utf16编码的字符串(js内置编码)
* @param {[String]} array GBK编码的数组
* @return {String} 转化后的utf16字符串
*/
GBKArrayToUTF16Str(array) {
if (!array) {
return '';
}
//res是用来存放结果的字符数组,最终会转为字符串返回
let res = [];
let len = array.length;
for (let i = 0; i < len; i++) {
let code = array[i];
//如果不是ASCII码转为unicode
if (code > 127) {
code = this.GBKCode2UniCode((code << 8) + array[++i]);
}
res.push(String.fromCharCode(code));
}
return res.join('');
};
参考资料: