如何在JavaScript中计算字符串的行数

119

我想要计算一个字符串中的行数。我尝试使用stackoverflow上的这个答案,

lines = str.split("\r\n|\r|\n"); 
return  lines.length;

对于这个字符串(最初是一个缓冲区):

 GET / HTTP/1.1
 Host: localhost:8888
 Connection: keep-alive
 Cache-Control: max-age=0
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.2 (KHTML,like Gecko) Chrome/15.0.874.121 Safari/535.2
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 Accept-Encoding: gzip,deflate,sdch
 Accept-Language: en-US,en;q=0.8
 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

出于某种原因,我得到了lines='1'

有什么办法让它工作吗?


3
@BookOfZeus的正则表达式处理"\n"和"\r","\n\r"是错误的。请注意,不能改变原文的含义。 - bezmax
哦,我明白了,你是对的,我的错。 - Book Of Zeus
我已经回答了一个相关的问题,“测试最少行数或标记的最快方法是什么?” http://stackoverflow.com/questions/39554154/fastest-way-to-test-for-a-minimum-number-of-lines-or-tokens - Joe Lapp
@bezmax:在粘贴文本时,“\n\r”是必要的。 - Supreme Dolphin
@SupremeDolphin 不是的,至少对于给出的示例不是。请参见https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_message:“请求行和其他标题字段必须以<CR><LF>结尾”,即\r\n - bezmax
11个回答

189

使用正则表达式可以统计行数,代码如下:

 str.split(/\r\n|\r|\n/).length

你还可以尝试下面的split方法。

var lines = $("#ptest").val().split("\n");  
alert(lines.length);

可行的解决方案:http://jsfiddle.net/C8CaX/


2
这个测试用例失败了:'Roomy Below:\n\nStart again.'。它检测到3行,但实际上有4行。这是因为split合并了两个换行符。 - SimplGy
7
@SimplGy 什么?它并没有失败。它之所以检测到3行,是因为有3行,即使在视觉上也是如此。 console.log('Roomy Below:\n\nStart again.') 给出了3行。如果拆分合并新行,这将不起作用:console.log('Roomy Below:\n\nStart again.'.split('\n').join('\n')),但实际上它确实起作用,你会再次获得相同的3行。 - Jools
1
你说得对,Jools。我在重新创建这个案例时搞砸了,因为从视觉上看它是三行(第一个 \n 结束了一行文本,第二个则创建了一个空行)。我相信我的反对意见在某个时候基于真实场景,但现在我不知道是什么情况了。 - SimplGy
4
如果您的文本只使用\n作为换行符(例如 <textarea>value),则可以考虑使用TEXT.match(/^/mg).length - Константин Ван
您的答案不正确,请考虑"\n\n"这种情况。只有两行,但是您的代码输出了3,这是不正确的。 - Khamidulla
1
@Khamidulla,你数错了,“\n\n”有3行,每行都包含一个空字符串。每个字符串(即使是空字符串)都有1行,然后每个\n会增加一行。这实际上与查看“1\n2\n3”相同,其中数字1、2和3分别位于它们自己的行上。 - scenia

80

另一个短小精悍的解决方案,可能比 split 更高效:

const lines = (str.match(/\n/g) || '').length + 1

为避免可能的错误,将其明确转换为字符串可能会有所帮助(https://dev59.com/B2445IYBdhLWcg3wZJcn#5196710):
const lines = (String(str).match(/\n/g) || '').length + 1

这是更好的解决方案。 - asmmahmud
3
像这个解决方案一样,有个小改进:\r? 实际上没有起到任何作用,(str.match(/\n/g) || '').length 可以得到相同的结果,对吧? - Samuel Kirschner
1
更好的解决方案,因为split函数会创建一个新数组,这比这个解决方案更耗费资源。 - hashed_name
2
两种方法都会创建一个新的数组... str.match 返回一个数组,split 也是如此... split 方法返回一个字符串数组,但是 str.match 返回一个对象数组。 我认为对象在内存中占用的空间比字符串更大... - Bruno Desprez
基准测试中,明显的获胜者。 - milahu
1
@milahu 对我来说,split的性能更好 :) 令人惊讶的是,for循环的性能如此之差。 - TWiStErRob

11

使用正则表达式进行拆分,可以使用/.../

lines = str.split(/\r\n|\r|\n/); 

同样的规则,但更短:/\r?\n/ - Alexander Yukal

11

嗯,是的...你正在做的事情完全是错误的。当你写 str.split("\r\n|\r|\n") 时,它会尝试查找确切的字符串 "\r\n|\r|\n"。这就是你的错误所在。整个字符串中不存在这样的出现。你真正想要的是 David Hedlund 建议的:

lines = str.split(/\r\n|\r|\n/);
return lines.length;

原因是在JavaScript中,split方法不会将字符串转换为正则表达式。如果想要使用正则表达式,请直接使用正则表达式。


8

我做了一个性能测试,比较了使用正则表达式、字符串和for循环来拆分文本的速度。

结果显示,使用for循环是最快的。

注意:这段代码对于Windows和macOS的换行符并不适用,但可以用来比较性能。

使用字符串进行拆分:

split('\n').length;

使用正则表达式进行分割:

split(/\n/).length;

使用for循环进行分割:

var length = 0;
for(var i = 0; i < sixteen.length; ++i)
  if(sixteen[i] == s)
    length++;

http://jsperf.com/counting-newlines/2


有趣的是,我的基准测试显示for循环最慢。 - milahu

3
有三种选择:
1. 使用jQuery(从jQuery官网下载)- jquery.com 2. 3.
var lines = $("#ptest").val().split("\n");
return lines.length;

使用正则表达式

var lines = str.split(/\r\n|\r|\n/);
return lines.length;

或者,重新创建一个for each循环。
var length = 0;
for(var i = 0; i < str.length; ++i){
    if(str[i] == '\n') {
        length++;
    }
}
return length;

这不是真正的注释。不仅有三个选项。 - Chris - Jr

2
更好的解决方案是使用str.match(/\n\g)函数,它只创建匹配元素的数组。而str.split("\n")函数则会创建由"\n"分割的新字符串数组,这样更加耗费资源。在我们的情况下,匹配的元素就是"\n"。"最初的回答"
var totalLines = (str.match(/\n/g) || '').length + 1;

2
使用展开运算符而不使用正则表达式来解决这个问题的另一个方法是:
const lines = [...csv].reduce((a, c) => a + (c === '\n' ? 1 : 0), 0)

const csv = `
demo_budget_2021_v4_wk_9,test,Civil,Spares,test,false,12,2021,100
demo_budget_2021_v4_wk_9,test,Civil,Spares,test,false,11,2021,100
demo_budget_2021_v4_wk_9,test,Civil,Spares,test,false,10,2021,100
demo_budget_2021_v4_wk_9,test,Civil,Spares,test,false,9,2021,100
`

const lines = [...csv].reduce((a, c) => a + (c === '\n' ? 1 : 0), 0)

console.log(lines);


1
这是一个示例代码 fiddle 只需从正则表达式中删除额外的 \r\n 和 "|"。

1
我正在测试函数的速度,我发现我写的这个解决方案比match要快得多。我们检查新字符串的长度与以前的长度相比是否有更改。
const lines = str.length - str.replace(/\n/g, "").length+1;

let str = `Line1
Line2
Line3`;
console.time("LinesTimer")
console.log("Lines: ",str.length - str.replace(/\n/g, "").length+1);
console.timeEnd("LinesTimer")


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