如何在JavaScript中将Unicode字符串拆分为字符

23

长期以来,我们在JS中使用天真的方法来分割字符串:

someString.split('');

但是,emoji表情的普及迫使我们改变了这种方法- 像😀这样的emoji字符(以及其他非BMP字符)由两个“字符”组成。

String.fromCodePoint(128514).split(''); // array of 2 characters; can't embed due to StackOverflow limitations

那么,现代、正确和高效的方法是什么呢?


我很好奇。你在谈论哪些StackOverflow的限制? - Mr Lister
似乎我无法发布带有JSON.stringify(String.fromCodePoint(128514).split(''))结果表达式的问题 - 这会导致jQuery抛出“格式不正确的URI”错误并禁止发布问题。 - Ginden
@MrLister:我已经添加了元帖 - Ginden
1
请查看 https://mathiasbynens.be/notes/javascript-unicode 了解 JavaScript Unicode 的全貌。 - georg
5个回答

23

使用数组字面量中的展开语法

const str = "";
console.log([...str]);

使用for...of循环:

function split(str){
  const arr = [];
  for(const char of str)
    arr.push(char)
   
  return arr;
}

const str = "";
console.log(split(str));


7
我认为需要注意的是,目前仍然无法涵盖许多正在使用的表情符号。例如 [...'‍♀️'] 变成 ["", "", "‍", "♀", "️"]。这意味着无法进行简单的字符串反转或逐个符号比较等操作。 - AndyO
1
请参考 https://github.com/orling/grapheme-splitter 作为一个库的例子,注意其中关于零宽连接符的问题。也许有更新的库可用。 - Manuel

10

这项任务的最佳方法是使用本地化的 String.prototype[Symbol.iterator],它能识别Unicode字符。因此,在字符串上使用Array.from是分割Unicode字符的一种简洁易行的方法,例如:

const string = String.fromCodePoint(128514, 32, 105, 32, 102, 101, 101, 108, 32, 128514, 32, 97, 109, 97, 122, 105, 110, 128514);
Array.from(string);

4
同样,[...'‍♀️'] 变为 ["", "", "‍", "♀", "️"] - srghma

8

JavaScript新增了一个API(作为ES2023的一部分),称为Intl.Segmenter,它允许您基于字形(字符串的用户感知字符)拆分字符串。 使用此API,您的拆分可能如下所示:

const split = (str) => {
  const itr = new Intl.Segmenter("en", {granularity: 'grapheme'}).segment(str);
  return Array.from(itr, ({segment}) => segment);
}
// See browser console for output
console.log(split('')); // ['']
console.log(split('é')); // ['é']
console.log(split('‍‍')); // ['‍‍']
console.log(split('❤️')); // ['❤️']
console.log(split('‍♀️')); // ['‍♀️']
<p>See browser console for logs</p>

这使您不仅可以处理由两个代码点构成的表情符号,例如,还包括其他字符,例如组合字符(例如),由ZWJ分隔的字符(例如‍‍),带有变量选择器的字符(例如❤️),带有表情符号修饰符的字符(例如‍♀️)等,所有这些字符都不能通过调用字符串迭代器(使用展开运算符...for..ofSymbol.iterator等)来处理,如其他答案中所述,因为这些方法只会迭代字符串的代码点。


1
点赞!不幸的是,根据此评论,在Firefox中不支持 - undefined

5

在ECMA 2015中引入了一个标志来支持正则表达式的Unicode感知。

u添加到您的正则表达式中,可以返回完整的字符作为您的结果。

const withFlag = `ABDE`.match(/./ug);
const withoutFlag = `ABDE`.match(/./g);

console.log(withFlag, withoutFlag);

关于它,这里有更多信息


1
相同的代码,'‍♀️'.match(/./ug) 将变成 ["", "", "‍", "♀", "️"] - srghma

0

我曾在某个地方做过类似的事情,需要支持旧版浏览器和ES5缩小器,这可能对其他人有用。

    if (Array.from && window.Symbol && window.Symbol.iterator) {
        array = Array.from(input[window.Symbol.iterator]());
    } else {
        array = ...; // maybe `input.split('');` as fallback if it doesn't matter
    }

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