表情、特殊字符、字符串截取

码元与码点

关于码元和和码点,通过一个例子进行介绍。

如图,字符串'😊'只有一个“笑脸”符号,但是通过length属性发现,“长度”为2,string.length到底表示什么?

答:码元的个数

什么是码元?码元就是编码的最小单元,UTF-16和UCS-2的码元为16个比特(2字节)。也就是说,'😊'使用了两个码元,也就是4字节进行编码。

通过string.charCodeAt(index)方法可以返回对应位置的码元。

那什么是码点呢?码点就是对应字符的编码,通过对应编码规则,将编码转换为1个或多个码元。

通过string.codePointAt(index)方法可以返回对应位置的码点。

 

这里再说回到Unicode和UCS-2、UTF-16等之间的关系。

Unicode编码,是统一的,对于一个确定的字符,他的Unicode编码就是确定的。就拿上面的例子来说,'😊'的Unicode编码为128522。那UTF-8、UTF-16等等这些编码是什么?这里更加容易理解的说法就是,他们都是Unicode编码的一种格式,也就是不同的“转换规则”。

这里使用了一个在线的编码工具https://www.qqxiuzi.cn/bianma/Unicode-UTF.php

可以看到,同样的Unicode编码,在UTF-8和UTF-16两种编码方式下,变成了完全不同的“格式”。但是,通过对应的编码规则“逆向”的推导,最终推导出Unicode的编码是一致的。

下面来看一下UTF-16怎么对'😊'进行编码:

由于输出时,默认使用10进制输出数字,为了看清楚码元和码点的关系,可以使用16进制输出。

 

可以看到,从码点编码为多个码元并不是简单的“组装”。UTF-16编码规则如下:

参考https://www.cnblogs.com/malecrab/p/5300503.html

如果码点(即字符编码)值大于0xFFFF,则将其减去0x10000,之后,在该数字的前10位(bits)加上0xD800,就得到UTF-16四字节编码中的前两个字节(第一个码元);该数字的后10位(bits)加上0xDC00,得到后两个字节(第二个码元)。

就拿上面的例子说:

'😊':Unicode编码位0x1F60A

0x1F60A - 0x10000 = 0xF60A = 0x0F60A = 0000 1111 0110 0000 1010

前10bits = 00 0011 1101 = 0x03D

后10bits = 10 0000 1010 = 0x20A

前10bits + 0xD800 = 0x03D + 0xD800 = 0xD83D

后10bits + 0xDC00 = 0x20A + 0xDC00 = 0xDE0A

可以看到,通过UTF-16的转换规则,将码点为0x1F60A的字符,转换为0xD83D和0xDE0A两个码元。

码元、码点和字符串相关的方法
1. 获取指定位置的码元

String.proto.charCodeAt(index)

上面也提到过,string.length返回码元的个数,所以这里的index有效范围是[0, length - 1]

2. 获取指定位置的字符(如果可以用一个码元进行编码,码元的值和码点的值相等)

String.proto.charAt(index)

charCodeAt()一个返回的是码元(数值),而charAt()返回码元对应的字符,如果一个字符有多个码元,这里仅仅取出其中一个码元,并把它当作码点,就会出现乱码。

3. 将字符串指定位置的码元转换成码点(如果是多个码元表示的码点,从指定位置开始,取多个码点进行转换)

String.proto.codePointAt(index)

如图,“笑脸”码元开始的位置是1;当传入2的时候,下标2位置对应的是“笑脸”的第二个码元,不是完整的两个码元开始的位置,所以直接返回了第2个码元,而不是转换成码点返回。

4.通过码元创建字符串

String.proto.charCodeFrom(...charCode)

将码元转换成字符串

5.通过码点创建字符串

String.proto.codePointFrom(...codePoint)

根据上面的解释我们可以得到最新的字符串截取函数

const str = '你好😊😂🤣😍哈哈';

//在String的原型上加pointLength用于判断长度
String.prototype.pointLength = function(){
    let len = 0;
    for (let i = 0; i < this.length;) {
        const codePoint = this.codePointAt(i);
        i += codePoint > 0xffff ? 2 : 1;
        len++;
    }
    return len;
}

//在String的原型上加pointAt用于获取指定下标内容
String.prototype.pointAt=function(index){
    let curIndex = 0;//码点的下标
    for (let i = 0; i < this.length;) {
        const codePoint = this.codePointAt(i);
        if (curIndex === index) {
            return String.fromCodePoint(codePoint);
        }
        i += codePoint > 0xffff ? 2 : 1;
        curIndex++;
    }
}

//在String的原型上加sliceByPoint用于截取字符串
String.prototype.sliceByPoint = function(start=0,end=this.pointLength()){
    let res = '';
    for (let i = start; i < end; i++) {
        res += this.pointAt(i);
    }
    return res;
}

console.log(str.pointLength());//8
console.log(str.pointAt(3));//😊
console.log(str.sliceByPoint(2,4));//😊😂

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值