js的unicode处理与转换(多字节问题)

Unicode

Unicode是目前绝大多数程序使用的字符编码,定义也很简单,用一个码点(code point)映射一个字符。码点值的范围是从U+0000到U+10FFFF,可以表示超过110万个符号。

字符集字数Unicode 编码
基本汉字20902字4E00-9FA5
基本汉字补充74字9FA6-9FEF
扩展A6582字3400-4DB5
扩展B42711字20000-2A6D6
扩展C4149字2A700-2B734
扩展D222字2B740-2B81D
扩展E5762字2B820-2CEA1
扩展F7473字2CEB0-2EBE0
扩展G4939字30000-3134A
康熙部首214字2F00-2FD5
部首扩展115字2E80-2EF3
兼容汉字477字F900-FAD9
兼容扩展542字2F800-2FA1D
PUA(GBK)部件81字E815-E86F
部件扩展452字E400-E5E8
PUA增补207字E600-E6CF
汉字笔画36字31C0-31E3
汉字结构12字2FF0-2FFB
汉语注音43字3105-312F
注音扩展22字31A0-31BA

UTF(Unicode transformation format)

UTF是Unicode转换格式,是服务于Unicode的,用于将一个Unicode码点转换为特定的字节序列。常见的UTF有

UTF-8 可变字节序列,用1到4个字节表示一个字符

UTF-16 可变字节序列,用2或4个字节表示一个字符

UTF-32 固定字节序列,用4个字节表示一个字符

UTF-8对ASCⅡ编码是兼容的,都是一个字节,超过U+07FF的部分则用了复杂的转换方式来映射Unicode,具体不再详述。

UTF-16对于BMP的码点,采用2个字节进行编码,用4个字节来表示。其中前两个字节范围是U+D800到U+DBFF,后两个字节范围是U+DC00到U+DFFF,通过以下公式完成映射(H:高字节 L:低字节 c:码点)

H = Math.floor((c-0x10000) / 0x400)+0xD800

L = (c – 0x10000) % 0x400 + 0xDC00

UCS(Universal Character Set)

UCS通用字符集,是一个ISO标准,目前与Unicode可以说是等价的。

相对于UTF,UCS也有自己的转换方法(编码)。如

UCS-2 用2个字节表示一个字符

UCS-4 用4个字节表示一个字符

UCS-2是一个过时的编码方式,因为它只能编码基本平面(BMP)的码点,在BMP的编码上,与UTF-16是一致的,所以可以认为是UTF-16的一个子集。

UCS-4则与UTF-32等价,都是用4个字节来编码Unicode。

JavaScript的字符处理

Js到底是用的啥编码呢?答案是UCS-2。咦,刚刚不是说UCS-2过时了吗?首先看下年表

1990 UCS-2 诞生

1995.5 Java 诞生

1996.7 UTF-16 诞生

也就是说,Brendan Eich在写JS的时候,UTF-16还没问世,所以只能用UCS-2的方式来处理字符,也因此留下了隐患。

隐患1:length问题

console.log("𠂒".length);//2

解决办法:

console.log(Array.from("𠂒").length);//1

隐患2:reverse的问题

function reverse(str) {
    return str.split('').reverse().join('');
}
console.log(reverse("𠂒是告的一部分"));
//分部一的告是��

解决办法:

function reverse(str) {
    //return str.split('').reverse().join('');
    return Array.from(str).reverse().join('');
}
console.log(reverse("𠂒是告的一部分"));
//分部一的告是𠂒

隐患3:unicode转码

//中文转Unicode
function encodeUnicode(str) {
    let res = [];
    for ( let i=0; i<str.length; i++ ) {
        let v = ( "00" + str.charCodeAt(i).toString(16) ).slice(-4)
        res[i] = v.toUpperCase();
    }
    return "\\u" + res.join("\\u");
}
console.log(encodeUnicode("告"))//\u544A,正确
console.log(encodeUnicode("𠂒"))//\uD840\uDC92,错误!实际编码:(0x)20092

//Unicode转中文
console.log(String.fromCharCode(0x544A));//告
console.log(String.fromCharCode(0xD840,0xDC92));//𠂒
console.log(String.fromCharCode(0x20092));//□ 无法识别的字符

解决办法:

//中文转Unicode
function encodeUnicode2(str) {
    let res = [];
    let i = 0;
    for(const char of str) {//不能使用length
        res[i++] = char.codePointAt(0).toString(16).toUpperCase();
    }
    return "\\u" + res.join("\\u");
}
console.log(encodeUnicode2("𠂒"))//\u20092

//Unicode转中文
console.log(String.fromCodePoint(0x20092));//𠂒

总结

长度判断:Array.from("𠂒").length

字符串循环遍历:for(const char of str)

反转:Array.from(str).reverse().join('')

中文转Unicode:char.codePointAt(0).toString(16) //代替char.charCodeAt(i).toString(16)

Unicode转中文:String.fromCodePoint(0x20092)  //代替String.fromCharCode(0x20092)

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值