如何将包含表情符号的字符串拆分为数组?

49

我想对一串表情符号进行操作,处理它们的各个字符。

在JavaScript中,"⛔".length == 13,因为"⛔"的长度是1,其他的长度都是2。所以我们无法进行如下操作:

var string = "⛔";
s = string.split(""); 
console.log(s);


1
http://mathiasbynens.be/notes/javascript-unicode#iterating-over-symbols - Mathias Bynens
7个回答

32

JavaScript ES6有一个解决方案,可以进行真正的切割:

[..."⛔"] // ["", "", "", "⛔", "", "", ""]

太棒了?但是当你通过你的转译器运行它时,可能不起作用(请参见@brainkim的评论)。 它只在符合ES6标准的浏览器上本地运行时才有效。 幸运的是,这包括大多数浏览器(Safari,Chrome,FF),但如果你正在寻找高浏览器兼容性,则这不是适合你的解决方案。


1
使用ES6设置的Babel将把它转译为对String的迭代器函数的调用,因此它可以在某些转译器中工作。 - brainkim
@brainkim 我在答案中已经指明了。这是转译器未达到标准的错误。 - Downgoat
啊,我是说有时候它可以工作。"当你通过你的转译器运行这个程序时,它不会工作" 意味着它从来没有工作过。这取决于字符串中具体的表情符号、你使用的转译器等因素。 - brainkim
40
抱歉,你提供的内容无法独立理解上下文含义。请提供更多上下文或相关信息以便我更好地为您进行翻译。 - BrunoLM
11
[...""] // ["", ""] - JJJ

27

编辑:有关库中的正确解决方案,请参见Orlin Georgiev的答案https://github.com/orling/grapheme-splitter


感谢这个答案,我制作了一个函数,它接受一个字符串并返回一个包含表情符号的数组:

var emojiStringToArray = function (str) {
  split = str.split(/([\uD800-\uDBFF][\uDC00-\uDFFF])/);
  arr = [];
  for (var i=0; i<split.length; i++) {
    char = split[i]
    if (char !== "") {
      arr.push(char);
    }
  }
  return arr;
};
所以
emojiStringToArray("⛔")
// => Array [ "", "", "", "⛔", "", "", "" ]

4
注意,这种方法对使用零宽连接器、变体选择器或键帽表情符号(由数字+键帽+变体选择器组成)的表情符号无效。 - Beau
只需使用 match 方法 str.match(/([\uD800-\uDBFF][\uDC00-\uDFFF])/); 即可返回表情符号。 - Edwin Reynoso
我尝试了你的函数,它对我有效,但是看看这个:emojiStringToArray("⛔❤️❤️❤️❤️❤️❤️") // => Array [ "", "", "", "⛔", "", "", "", "❤️❤️❤️❤️❤️❤️" ]你知道如何解决这个错误吗? - Sebastián Lara
15
emojiStringToArray( '‍‍‍' ) // ["", "‍", "", "‍", "", "‍", ""]这段代码的意思是将包含 Emoji 表情的字符串转换为数组。具体而言,它将字符串 '‍‍‍' 转换为一个包含七个元素的数组,每个元素都代表一个表情符号。在这个示例中,这是一个包含三个人的家庭表情符号。数组中的空字符串表示这些表情符号之间没有其他字符。 - BrunoLM

25
使用即将推出的Intl.Segmenter,您可以这样做:
const splitEmoji = (string) => [...new Intl.Segmenter().segment(string)].map(x => x.segment)

splitEmoji("⛔") // ['', '', '', '⛔', '', '', '']

这也解决了与“‍‍‍”和“”相关的问题。
splitEmoji("‍‍‍") // ['‍‍‍', '']

根据CanIUse的数据,除了IE和Firefox之外,截至目前全球有91.23%的用户支持此功能。
在Firefox获得支持之前,正如Matt Davies' answer中提到的,Graphemer是最佳解决方案。
let Graphemer = await import("https://cdn.jsdelivr.net/npm/graphemer@1.4.0/+esm").then(m => m.default.default);
let splitter = new Graphemer();
let graphemes = splitter.splitGraphemes("‍‍‍"); // ['‍‍‍', '']

看起来很有前途。 - cmgchess

22

这个图形分隔库做到了这一点,甚至与旧浏览器完全兼容,并且不仅适用于表情符号,还适用于各种奇特字符: https://github.com/orling/grapheme-splitter 在自己编写的解决方案中,你很可能会忽略边缘情况。而这个库实际上基于UAX-29 Unicode标准。


13

将UTF8字符串分割的现代/正确方法是使用Array.from(str)而不是str.split('')


1
这太棒了。MDN也为此提供了一个polyfill。请参见:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from - Hooman Askari
11
遗憾的是,对于复合字符,它并不能像预期的那样工作:Array.from('‍‍‍'); // [ "", "‍", "", "‍", "", "‍", "" ]Array.from(''); // [ "", "" ] - forresto

11

这个由Orlin Georgiev开发的Grapheme Splitter库非常棒。

尽管它已经有一段时间没有更新了,目前(2020年9月)只支持Unicode 10及以下版本。

如果您需要支持Unicode 13版本的最新版本Grapheme Splitter,请查看使用Typescript构建的https://github.com/flmnt/graphemer

下面是一个快速示例:

import Graphemer from 'graphemer';

const splitter = new Graphemer();

const string = "⛔";

splitter.countGraphemes(string); // returns 7

splitter.splitGraphemes(string); // returns array of characters

该库也可以使用最新的表情符号。

例如:"‍".length === 7,但是splitter.countGraphemes("‍") === 1

完全公开:我创建了该库并更新到Unicode 13版本。该API与Grapheme Splitter相同,并且完全基于该工作,只是将其更新到最新版本的Unicode,因为原始库已经有几年没有更新,似乎不再维护。


1
Intl.Segmenter 得到 Firefox 支持之前(https://caniuse.com/mdn-javascript_builtins_intl_segmenter),我认为这是最好的答案。 - forresto

8
可以使用正则表达式的u标志来完成。正则表达式为:
/.*?/u

每次至少有零个或多个字符,可以是表情符号,但不能是空格或换行符,这会导致其无法正常工作。
  • 至少最小为零个: ? (分割成零个字符)
  • 零个或多个: *
  • 不能是空格或者换行符: .
  • 可能是表情符号: /u
通过使用问号?,我强制每次切断恰好为零个字符,否则/.*/u它将根据所有字符进行分割,直到找到一个空格或换行符。

var string = "⛔"
var c = string.split(/.*?/u)
console.log(c)


6
''.split(/.*?/u); // [ "", "" ]这段代码的翻译是:将空字符串按照非贪婪模式正则表达式 /.*?/u 分割,结果为数组 [ "", "" ] - forresto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接