emoji表情和复杂字符长度截取

612 阅读1分钟

码元

码元是字符编码系统(例如 UTF-8 或 UTF-16)使用的基本组成部分。字符编码系统将一个 Unicode 码位编码为一个或者多个码元。

JavaScript中字符串长度

以字符占位的码元为准。utf-8中普通字符(Unicode<=65535)占一个码元,其余字符占两个码元。

console.log('我'.length) // 1
console.log('𠮷'.length) // 2
console.log('👨‍👩‍👦‍👦'.length) // 11

emoji表情

单个表情占两个码元

console.log('👨'.length) // 2

合成表情占码元 = 单个表情数 * 2 + 连接符数 * 1

console.log('👩‍👦'.length) // 5

获取字符串真实字节长度

字符UTF-8占用字节数判断

function getUTF8CodeLength(word) {
  /** *****
   *  UNICODE    UTF-8
   *	00000000 - 0000007F 0xxxxxxx //占一个字节
   *	00000080 - 000007FF 110xxxxx 10xxxxxx //占两个字节
   * 	00000800 - 0000FFFF 1110xxxx 10xxxxxx 10xxxxxx //占三个字节
   *	00010000 - 001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx //占四个字节
   *	00200000 - 03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx //占五个字节
   *	04000000 - 7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx //占六个字节
   * ****/
  const unicodeSplit = [0, 0x00000080, 0x00000800, 0x00010000, 0x00200000, 0x04000000];
  const unicode = word.codePointAt(0); // 获取字符Unicode
  return unicodeSplit.length - unicodeSplit.reverse().findIndex(item => unicode > item);
}
console.log(getUTF8CodeLength('A')); // 1
console.log(getUTF8CodeLength('£')); // 2
console.log(getUTF8CodeLength('我')); // 3
console.log(getUTF8CodeLength('𦓡')); // 4
console.log(getUTF8CodeLength('👨')); // 4

合成emoji表情字符长度判断

前面有介绍合成表情码元占位 合成emoji字符字节长度 = 单个emoji * 4 + 连接符数 * 3

// 获取准确的字符串码元长度
function getWordLength(word, maxLen = Infinity) {
  const wordLength = word.length || 0;
  let totalCount = 0;
  let flag = false; // 是否是处于合成表情中
  let endPosition = 0; // 字符串的应该展示的码元长度

  for (let i = 0; i < wordLength; i++) {
    const code = word.codePointAt(i).toString(16);
    const isSimple = code.length <= 4; // 是否小于等于0xFFFF即65535,是则为简单字符
    // 复杂字符串处理
    if (!isSimple) {
      // 复杂字符占两个码元
      const currentStr = [word[i], word[i + 1]].join('');
      totalCount += getUTF8CodeLength(currentStr);
      i++;

      // 若下下个字节存在判断其是否是合成表情
      if (i + 1 < wordLength) {
        flag = word.codePointAt(i + 1).toString(16) === emojiJoinCode;
      }
    } else {
      totalCount += getUTF8CodeLength(word[i]);
    }

    // 超出最大限制
    if (totalCount > maxLen) {
      break;
    }

    // 已循环遍历完码元
    if (i === wordLength - 1 && totalCount <= maxLen) {
      endPosition = wordLength;
      break;
    }

    // 存在合成表情连接符则继续遍历
    if (flag) {
      continue;
    }
    endPosition = i + 1;
  }

  return {
    totalCount,
    endPosition
  };
}