JavaScript 正则表达式:按行分割

93

如何将长文本分成多行?为什么这里的结果会返回两次 line1

/^(.*?)$/mg.exec('line1\r\nline2\r\n');

["line1", "line1"]

我打开了多行模式,以使得^$匹配每一行的开头和结尾。我同时打开了全局模式,以捕获所有行。

我想使用正则表达式分割(regex split),而不是String.split,因为我需要处理Linux的\n和Windows的\r\n行结束符。

7个回答

161
arrayOfLines = lineString.match(/[^\r\n]+/g);

正如Tim所说,它既匹配整个字符串又捕获其中的一部分。 regex.exec(string)似乎会在找到第一个匹配项后返回,而不管全局修饰符,而string.match(regex)则会遵守全局修饰符。


12
请注意,Tim的程序将匹配空行,而我的程序则不会。这两种情况可能都有其优缺点。 - ReactiveRaven
虽然这个回答有点老,但我想说exec返回第一个匹配项的原因是因为它旨在为全局正则表达式调用多次,直到返回null,并且正则表达式存储像lastIndex这样的东西,即下一次匹配开始的索引。 - iPherian
1
尝试运行 "123\n\n1234".match(/[^\r\n]+/g);,期望结果为 Array [ "123", "", "1234" ],但实际得到的是 Array [ "123", "1234" ] - sea-kg

119

使用

result = subject.split(/\r?\n/);

你的正则表达式返回了line1两次,因为line1既是整个匹配结果又是第一个捕获组的内容。


4
你需要使用 g 标志,并且在一些旧的苹果机器上,\r 是一个有效的换行符。此外, Unicode 定义了 \u2028\u2029 和旧的 IBM 换行符 \u0085 作为换行符。因此,/[\n\u0085\u2028\u2029]|\r\n?/g 可以处理所有边界情况。 - Mike Samuel
7
@Mike:你确定要使用/g标志吗?除非明确说明,否则拥有只分割一次的分割函数是没有意义的。而且Jojo说他只处理Linux和Windows。接下来是EBCDIC吗? - Tim Pietzcker
4
@Mike: 不需要使用/g标志。你可以加上它,但JavaScript会忽略它。就像Tim所说的那样, 默认行为是尽可能地分割字符串,但你可以使用第二个参数来限制最大次数。 - Alan Moore
28
关于何为换行符,情况甚至比这更糟。根据Unicode联盟的规定,我们应该始终使用(\r\n|[\n\v\f\r\x85\u2028\u2029]),无论软件运行在哪个平台上,或者数据来自哪里。 - Alan Moore
Match 比 Split 快得多:http://jsperf.com/regex-split-vs-match - Wilt
显示剩余2条评论

28

我假设以下内容表示换行:

  1. \r 后跟 \n
  2. \n 后跟 \r
  3. 仅出现 \n
  4. 仅出现 \r

请使用:

var re=/\r\n|\n\r|\n|\r/g;

arrayofLines=lineString.replace(re,"\n").split("\n");

对于包括空行在内的所有行的数组。

或者

请使用。

arrayOfLines = lineString.match(/[^\r\n]+/g); 

对于一个非空行的数组


\n后跟着\r不是单个换行符。 - JLRishe
它在某些平台上存在。如果你在C#中检查Environment.NewLine,你会看到\n\r。 - Gravity API

24

更简单的正则表达式可以处理所有行尾组合,即使在同一文件中混合,并且还会删除空行:

var lines = text.split(/[\r\n]+/g);

带有空格修剪:

var lines = text.trim().split(/\s*[\r\n]+\s*/g);


1
第一个函数可以删除文本中间的空行,但不会删除开头或结尾的空行。对于我的目的来说这是可以接受的,我只是想提醒需要保持一致性的人注意这一点。 - twm

8

Unicode兼容的换行

Unicode®技术标准#18定义了什么构成了行边界。同一部分还提供了一个正则表达式来匹配所有的行边界。使用该正则表达式,我们可以定义以下JS函数,将给定字符串在任何行边界处拆分(保留空行以及前导和尾随空格):

const splitLines = s => s.split(/\r\n|(?!\r\n)[\n-\r\x85\u2028\u2029]/)

我不明白为什么需要负向前瞻部分((?!\r\n)),但这是Unicode文档建议的。

上述文档建议定义一个正则表达式元字符来匹配所有的行尾字符和序列。Perl使用\R来实现。不幸的是,JavaScript没有包含这样的元字符。遗憾的是,我甚至找不到TC39的提案。


6

首先将所有的\r\n替换为\n,然后使用String.split函数。


这需要两个命令。能否用一个命令的正则表达式完成? - JoJo
2
@JoJo:myString.replace(/\r\n/, "\n").split("\n")(除非你是出于学术兴趣在问这个问题 :)) - Tim
'line1\r\nline2\r\n'.replace(/\r\n/, '\n').split('\n').without(''); 产生了一个错误的第二个单元格: ["line1", "line2\r"] - JoJo
@JoJo:抱歉,我忘记了全局的 /g 标志!应该是:myString.replace(/\r\n/g, "\n").split("\n") - Tim
好的,但我认为正则表达式应该让我在一行中简洁地完成任务。我相信所有的正则表达式都可以被简化成一堆“替换”和“分割”。 - JoJo
3
正则表达式并不是适用于所有任务的工具。它们非常强大,但不应该被无处不用地使用。请注意,replace本身就是一个正则表达式。@Jojo: 这句话简洁明了,只有一行 :) - Tim

1

1
这会将行分隔符保留在末尾。 - sean

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