javascript如何截取字符串,不出现乱码

直接实现的方式

String.prototype.sliceByPoint = function (pStart, pEnd) {
  let result = ""; // 截取的结果
  let pIndex = 0; // 码点的指针
  let cIndex = 0; // 码元的指针
  // eslint-disable-next-line no-constant-condition
  while (true) {
    if (pIndex >= pEnd || cIndex >= this.length) {
      // 如果码点起始指针位置大于要截取的最大长度(结束)或者码元的起始指针大于整个字符串的长度(结束)
      break;
    }
    const point = this.codePointAt(cIndex);
    if (pIndex >= pStart) {
      result += String.fromCodePoint(point);
    }
    pIndex++;
    cIndex += point > 0xffff ? 2 : 1;
  }
  return result;
};
testee() {
      const str = "阿是𠮷娃娃";

      console.log("1",str.slice(0, 3));
      console.log("2",str.sliceByPoint(0, 3));
      console.log("3",str);
      console.log(0xffff);
    },

执行结果为

 

码元与码点

JavaScript字符串使用了两种Unicode编码混合的策略:UCS-2和UTF-16。对于可以菜哦也能够16位编码的字符(U+0000~U+FFFF),这两种编码实际上是一样的。(《JavaScript高级程序设计》)

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

如图,字符串'😊'只有一个“笑脸”符号,但是通过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编码的一种格式,也就是不同的“转换规则”。

这里使用了一个在线的编码工具Unicode编码转换,UTF编码转换(UTF-8、UTF-16、UTF-32) 

如果码点(即字符编码)值大于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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值