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('');
	};


参考资料:

GBK编码范围

汉字字符集编码查询