码元
码元是字符编码系统(例如 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
};
}