js:如何截取含有表情的字符串

蚊子前端博客
发布于 2018-12-01 00:15
若字符串中出现一些特殊字符,我们该如何截取才能不出现乱码的现象呢?

在以QQ和微信作为第三方登录的系统中,用户的昵称里经常会有表情等字符,比如这样的:非拉🍒非拉。如果项目中正好有个需要将用户昵称强行按照字符个数进行截断的需求,你会发现截取的字符串中会有乱码的现象。

以上面的用户昵称 非拉🍒非拉 为例,我们算下这个字符串的长度。

因html编码的原因,在以下字符串和代码中,无法正常的显示出樱桃的图像,全部使用Y进行代替,测试时,请将Y替换为具体的樱桃符号

COPYJAVASCRIPT

let nickname = '非拉Y非拉'; nickname.length; // 6

说明中间的表情字符占了2个字符的长度,如果单独输出nickname[2]或者nickname[3]时,均会输出乱码字符。

JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符,JavaScript 会认为它们是两个字符。

如果针对所有的昵称都要截取前3个字符时,该怎么截取呢?如果是中文、英文、数字、标点符号等常见字符,那么直接从3开始截取就行了,如果上面的那种有特殊符号的怎么办呢?

1. Array.from方法

Array.from这个方法能够将类数组转换为真实的数组,比如NodeList, argument等,同样,也包括字符串。

COPYJAVASCRIPT

Array.from(nickname); // ["非", "拉", "Y", "非", "拉"] nickname.split(''); // ["非", "拉", "�", "�", "非", "拉"]

使用Array.from把nickname转换后,可以看到转换成一个真实的数组了,樱桃字符占了数组中的一个位置,然后按照数组中的方法截取再进行拼接即可,而使用split方法拆分,则还是乱码:

COPYJAVASCRIPT

function truncated(str, num){ return Array.from(str).slice(0, num).join(''); } truncated(nickname, 3); // 非拉Y

2. codePointAt()方法

在ES6之前, JS 的字符串以 16 位字符编码(UTF-16)为基础。每个 16 位序列(相当于2个字节)是一个编码单元(code unit),可简称为码元,用于表示一个字符。字符串所有的属性与方法(如length属性与charAt() 方法等)都是基于16位序列。

比如length方法、nickname[2]、split方法等操作,都会产生异常。为此在ES6中,加强了对 Unicode 的支持,并且扩展了字符串对象。

对于 Unicode 码点大于0xFFFF的字符,是使用4个字节进行存储。ES6 提供了codePointAt方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。

COPYJAVASCRIPT

// 获取樱桃的码点 "Y".codePointAt(0).toString(16); // 1f352 // 输出码点对应的字符 "\u{1f352}"; // Y

请注意: 在之前Unicode编码,均在[\u000-\uFFFF]之间,因此可以使用类似\u0047这样的编码;但是现在码点超过\uFFFF的界限,若再这样使用,则获取不到对应的字符。因此在ES6中,码点的字符放在中括号内,类似上面的格式(所有的码点均可以使用这种格式):

COPYJAVASCRIPT

"\u{1f352}"; // Y "\u{47}" // G "\u{0047}" // G

那么就容易了:判断需要截取的位置是否正好是4字节的字符,如果是则延长一位截取,否则正常截取:

COPYJAVASCRIPT

function truncated(str, num){ let index = Array.from(str)[num-1].codePointAt(0)>0xFFFF ? num+1 : num; return str.slice(0, index); } truncated(nickname, 3); // 非拉Y

3. for-of

for-in方法是遍历key值,for-of是遍历value值:

COPYJAVASCRIPT

let arr = ['a', 'b', 'c']; for(let k in arr){ console.log(k); // 0 1 2 } for(let v of arr){ console.log(v); // a b c } for(let v of nickname){ console.log(v); // 非 拉 Y 非 拉 }

因此利用这个功能,我们也能进行截取:

COPYJAVASCRIPT

function truncated(str, num){ let s = ''; for(let v of nickname){ s += v; num--; if(num<=0){ break; } } return s; } truncated(nickname, 3);

总结

实现的方法有很多种,就看你能不能再多想一点点!

最后,给大家出一个小题:

实现一个函数,传入两个值,第1个值是一个数组arr,第2个值是要实现的和target,请在数组arr中找到两个值,使这两个值的和正好等于target,最后返回这两个值的下标的数组。

比如传入的两个值为: [1, 7, 3, 2], 4 。 那么可以看到1+3等于4,对应的下标分别是0和2,因此返回[0, 2]。

请注意: 输入的数组中一定且只会存在两个值的和等于target!

关于这个题目,你有多少种解法呢?

阅读(2012) 评论(0)

公众号:

qrcode

微信公众号:前端小茶馆