我怎样能从一个字符串中获取字符数组?

484
如何在JavaScript中将字符串转换为字符数组?
我想获取一个像"Hello world!"这样的字符串,并将其转换为数组
['H','e','l','l','o','','w','o','r','l','d','!']
15个回答

565

注意:这不符合Unicode标准。 "IU".split('') 的结果是一个由4个字符组成的数组 ["I", "�", "�", "u"],可能会导致严重的错误。请查看下面的答案以获得安全可靠的替代方法。

只需用空字符串进行分割即可。

var output = "Hello world!".split('');
console.log(output);

查看String.prototype.split()MDN文档


39
这并未考虑代理对。"".split('') 的结果为 ["�", "�"] - hippietrail
89
请参考 @hakatashi 在此帖子中的回答。希望大家看到这条信息...不要使用这种方法,它不安全unicode。 - i336_
3
有点晚了,但是为什么有人想要将字符串制作成数组?字符串本身已经是一个数组了,或者我错了吗?"randomstring".length; //12 "randomstring"[2]; //"n" - Luigi van der Pal
8
一个字符串不是一个数组,但它非常相似。然而,它与字符数组不相似。一个字符串类似于一个16位数字的数组,其中一些表示字符,另一些表示代理对中的一半。例如,str.length并不能告诉你字符串中字符的数量,因为某些字符需要更多的空间; str.length 告诉你的是16位数字的数量。 - Theodore Norvell

422

正如hippietrail建议的那样, meder的回答可以打破代理对并误解“字符”。例如:

// DO NOT USE THIS!
const a = ''.split('');
console.log(a);
// Output: ["�","�","�","�","�","�","�","�"]

我建议使用以下ES2015功能来正确处理这些字符序列。

展开语法(已由insertusernamehere回答

const a = [...''];
console.log(a);

Array.from

const a = Array.from('');
console.log(a);

正则表达式 u 标志

const a = ''.split(/(?=[\s\S])/u);
console.log(a);

使用/(?=[\s\S])/u代替/(?=.)/u,因为.不匹配换行符。如果您仍处于ES5.1时代(或者您的浏览器无法正确处理此正则表达式-例如Edge),则可以使用以下替代方案(由Babel转译)。请注意,Babel还尝试正确处理未匹配的代理项。但是,这似乎对未匹配的低代理项无效。

const a = ''.split(/(?=(?:[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))/);
console.log(a);

for ... of ... 循环结构

const s = '';
const a = [];
for (const s2 of s) {
   a.push(s2);
}
console.log(a);


14
请注意,此解决方案会将某些表情符号(例如️‍)拆分开,并将附加的重音符号从字符中拆分出来。如果您想要将其拆分为字形簇而不是字符,请参见https://dev59.com/w2w15IYBdhLWcg3wo9Rx#45238376。 - user202729
8
请注意,虽然不拆分代理对是很好的,但这并不是保持“字符”(或更准确地说,图形符号)在一起的通用解决方案。一个图形符号可以由多个代码点组成;例如,语言德瓦納格里的名称是“देवनागरी”,本地人读作五个图形符号,但需要八个代码点来生成... - T.J. Crowder
12
这个答案被官方 Mozilla 文档所引用,网址为 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split。 - Zefiro

96

spread语法

您可以使用spread语法,一种在ECMAScript 2015(ES6)标准中引入的数组初始化器:

var arr = [...str];

示例

function a() {
    return arguments;
}

var str = 'Hello World';

var arr1 = [...str],
    arr2 = [...'Hello World'],
    arr3 = new Array(...str),
    arr4 = a(...str);

console.log(arr1, arr2, arr3, arr4);

前三个结果为:

["H", "e", "l", "l", "o", " ", "W", "o", "r", "l", "d"]

最后一个导致

{0: "H", 1: "e", 2: "l", 3: "l", 4: "o", 5: " ", 6: "W", 7: "o", 8: "r", 9: "l", 10: "d"}

浏览器支持

请查看ECMAScript ES6 兼容性表


进一步阅读

spread也被称为"splat"(例如在PHPRuby中),或者作为"scatter"(例如在Python中)。


演示

试用后购买


2
如果您在使用扩展运算符时结合编译器转换为ES5,则在IE中无法正常工作。请考虑这一点。我花了几个小时才找出问题所在。 - Stef van den Berg

23

在编程中,我们可以把“字符”看做至少三个不同的事物,并因此采用三个不同的方法来处理。

将字符串拆分为UTF-16码单元

JavaScript字符串最初是作为一系列UTF-16码单元发明的,在当时UTF-16码单元与Unicode码点之间存在一对一的关系。字符串的.length属性测量它在UTF-16码单元中的长度,当你使用someString[i]时,可以得到someString的第i个UTF-16码单元。

因此,您可以使用类似C语言风格的for循环和索引变量来从一个字符串获取UTF-16码单元的数组...

const yourString = 'Hello, World!';
const charArray = [];
for (let i=0; i<yourString.length; i++) {
    charArray.push(yourString[i]);
}
console.log(charArray);

还有一些简单的方法可以实现相同的功能,比如使用空字符串作为分隔符来使用 .split()

const charArray = 'Hello, World!'.split('');
console.log(charArray);

如果字符串包含由多个UTF-16代码单元组成的代码点,那么这将把它们拆分为单个代码单元,这可能不是你想要的。例如,字符串''由四个Unicode代码点(代码点0x1D7D8到0x1D7DB)组成,在UTF-16中,每个代码点由两个UTF-16代码单元组成。如果我们使用上述方法拆分该字符串,我们将得到一个由八个代码单元组成的数组:

const yourString = '';
console.log('First code unit:', yourString[0]);
const charArray = yourString.split('');
console.log('charArray:', charArray);

分割为Unicode代码点

也许我们想将字符串分割为Unicode代码点!这在ECMAScript 2015中添加了一个可迭代对象的概念后就成为可能。现在字符串是可迭代的,在迭代它们时(例如使用for...of循环),你会得到Unicode代码点,而不是UTF-16代码单元:

const yourString = '';
const charArray = [];
for (const char of yourString) {
  charArray.push(char);
}
console.log(charArray);

我们可以使用Array.from来缩短代码,它会隐式地迭代传递给它的iterable对象:

const yourString = '';
const charArray = Array.from(yourString);
console.log(charArray);

然而,Unicode 代码点也不是可能被认为是一个“字符”的最大可能的东西之一。一些可以合理地被认为是单个“字符”但由多个代码点组成的例子包括:

  • 带有组合码点的重音字符
  • 旗帜
  • 一些表情符号

我们可以看到,如果我们尝试通过上面的迭代机制将具有这些字符的字符串转换为数组,则结果数组中的字符会被分解。 (如果您的系统上没有呈现任何字符,则下面的yourString由一个带有锐音符的大写字母A,后跟英国国旗,后跟一个黑人女性组成。)

const yourString = 'Á';
const charArray = Array.from(yourString);
console.log(charArray);

如果我们想要将每个字符作为最终数组中的单个项目保留,那么我们需要一个由字形组成的数组,而不是代码点。

分割成字形

JavaScript没有内置支持此功能 - 至少目前还没有。因此,我们需要一个理解并实现Unicode规则的库,以确定哪些代码点的组合构成一个字形。幸运的是,这样的库已经存在:orling的grapheme-splitter。您可以使用npm安装它,或者如果您不使用npm,则下载index.js文件并使用<script>标签提供服务。对于此演示,我将从jsDelivr加载它。

grapheme-splitter为我们提供了一个GraphemeSplitter类,其中包含三种方法:splitGraphemesiterateGraphemescountGraphemes。自然地,我们想要使用splitGraphemes

const splitter = new GraphemeSplitter();
const yourString = 'Á';
const charArray = splitter.splitGraphemes(yourString);
console.log(charArray);
<script src="https://cdn.jsdelivr.net/npm/grapheme-splitter@1.0.4/index.js"></script>

现在我们得到了一个由三个字形组成的数组,这很可能是你想要的。


这真的很有帮助。在我正在进行的项目中真的为我节省了很多时间。谢谢!!! - raddevus

22
你可以使用 Array.from

var m = "Hello world!";
console.log(Array.from(m))

这个方法已经在ES6中被引入。

参考资料

Array.from

Array.from() 静态方法可以从可迭代对象(iterable)或类数组对象(array-like)中创建一个新的浅拷贝的 Array 实例。


11
你可以使用Object.assign函数来获得所需的输出:

var output = Object.assign([], "Hello, world!");
console.log(output);
    // [ 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!' ]

这并不一定对或错,只是另一个选择。

Object.assign在MDN网站上有很好的描述


2
那是一个绕远路的方式来得到 Array.from("Hello, world") - T.J. Crowder
1
@T.J.Crowder 这是一个绕远路的方式,才能得到 [..."Hello, world"] - chharvey
Object.assign([], "")[ "\ud83e", "\udd8a" ]。这显然是错误的。 - Sebastian Simon
@SebastianSimon 原始问题并不是那种情况。 - David Thomas
1
@DavidThomas 这里没有“用例”。这是完全相同的字符串处理和编码。这是关于在实际代码中实现解决方案。现在有一个解决方案可以在90%的情况下工作,还有一个解决方案可以在100%的情况下工作,但没有额外的成本 - 所以显然要选择哪个。Array.from等是正确的解决方案,而Object.assign等则是不正确的解决方案。提供仅在有限情况下勉强工作的解决方案的情感对软件行业是有害的。 - Sebastian Simon

10

它已经是:

var mystring = 'foobar';
console.log(mystring[0]); // Outputs 'f'
console.log(mystring[3]); // Outputs 'b'

或者,如果要兼容较旧的浏览器,请使用以下代码:

var mystring = 'foobar';
console.log(mystring.charAt(3)); // Outputs 'b'


4
不是这样的。试试这个:alert("Hello world!" == ['H','e','l','l','o',' ','w','o','r','l','d']) - R. Martinho Fernandes
5
抱歉,我想表达的是:“你可以通过索引引用来访问单个字符,而无需创建字符数组”。请问这句话需要翻译成中文吗? - dansimau
3
无法在所有浏览器上可靠地实现。这是 ECMAScript 第五版的特性。 - bobince
8
跨浏览器版本为mystring.charAt(index) - psmay
1
虽然我更喜欢使用类数组的变量,但对于 charAt() 函数我会给一个赞。该死的 IE。 - Zenexer
显示剩余3条评论

7

在JavaScript中,您可以通过以下四种方式将字符串转换为字符数组:

const string = 'word';

// Option 1
string.split('');  // ['w', 'o', 'r', 'd']

// Option 2
[...string];  // ['w', 'o', 'r', 'd']

// Option 3
Array.from(string);  // ['w', 'o', 'r', 'd']

// Option 4
Object.assign([], string);  // ['w', 'o', 'r', 'd']

1
如果测试字符串包含大于U+FFFF的Unicode代码点,则此答案将更好。 "".split("")Object.assign([], "")分别为[ "\ud83e", "\udd8a" ](由于字符串是UTF-16编码的,因此必要时按其代理字节对进行索引); [ ..."" ]Array.from("")是等效的,并且结果为[ "" ](由于这两种方法访问Symbol.iterator属性,该属性具有Unicode感知能力)。 - Sebastian Simon

5
ES6 中将字符串按字符拆分成数组的方法是使用扩展运算符。它简单而优雅。
array = [...myString];

例子:

let myString = "Hello world!"
array = [...myString];
console.log(array);

// another example:

console.log([..."another splitted text"]);


4

正如Mark Amery在他的优秀回答中指出的那样,仅仅按代码点进行分割可能是不够的,尤其是对于特定的表情符号或组合字符(例如:由两个代码点ñ 组成,形成一个字形)。JavaScript有一个内置的字形分段器,可通过国际化API(Intl)调用,名为Intl.Segmenter。它可以用于按不同的粒度分段字符串,其中之一是字形(即字符串的用户感知字符):

const graphemeSplit = str => {
  const segmenter = new Intl.Segmenter("en", {granularity: 'grapheme'});
  const segitr = segmenter.segment(str);
  return Array.from(segitr, ({segment}) => segment);
}
// See browser console for output
console.log("Composite pair test", graphemeSplit("foo  bar mañana mañana"));
console.log("Variation selector test", graphemeSplit("❤️"));
console.log("ZWJ Test:", graphemeSplit("‍❤️‍‍"));
console.log("Multiple Code Points:", graphemeSplit("देवनागरी"));


1
也可以考虑使用 normalize - Sebastian Simon
1
@SebastianSimon 谢谢 - 对的,规范化是处理可以预组合字符(例如上面的 )的不错选项。然而,它无法适用于由多个代码点组成的所有类型字符(例如表情符号),但如果字符串由可组合的代码点组成,则是一个很好的选择。 - Nick Parsons
1
学习这个非常有趣!不幸的是,Can I use显示Intl.Segmenter在Firefox中还没有任何支持,因此,除非我能找到一个好的polyfill,否则我不想在公共面向网页上使用它。阅读完这个答案后,我仍然对两件事情感到不确定:首先,如果想在公共网络上使用它,是否存在一个好的polyfill,其次,为什么Intl.Segmenter需要一个语言环境参数,它有什么影响? - Mark Amery
1
@MarkAmery,@formatjs Intl.Segmenter polyfill:https://github.com/formatjs/formatjs/tree/main/packages/intl-segmenter 也可以参考 https://dev59.com/XHNA5IYBdhLWcg3wSrqa#76777557 - mrienstra

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