How to count the number of lines of a string in javascript
Asked Answered
C

12

121

I would like to count the number of lines in a string. I tried to use this stackoverflow answer,

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

on this string (which was originally a buffer):

 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

and, for some reason, I got lines='1'.

Any idea how to make it work?

Crossbill answered 13/12, 2011 at 11:49 Comment(5)
@BookOfZeus "\n" and "\r" are handled by his regexp. "\n\r" is plain wrong.Depicture
oh i see it, you are right my badLacielacing
I've answered a related question, "What's the fastest way to test for a minimum number of lines or tokens?" #39554654Johm
@Depicture "\n\r" is necessary for pasted text.Grandpa
@SupremeDolphin No it is not, at least not for the example given. See en.wikipedia.org/wiki/… : "The request line and other header fields must each end with <CR><LF>", that is \r\n.Depicture
P
193

Using a regular expression you can count the number of lines as

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

Alternately you can try split method as below.

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

working solution: http://jsfiddle.net/C8CaX/

Pearsall answered 13/12, 2011 at 11:54 Comment(6)
Fails for this test case: 'Roomy Below:\n\nStart again.'. It detects 3 lines, when, visually, there are 4. This is because the split merges both new lines together.Animatism
@Animatism What? It doesn't fail. It detects 3 lines because there are 3 lines, even visually. console.log('Roomy Below:\n\nStart again.') gives you 3 lines. If split merged new lines, this wouldn't work: console.log('Roomy Below:\n\nStart again.'.split('\n').join('\n')), but it does and you get the same 3 lines again.Punjabi
You're right Jools, I messed up this re-creation case because visually that is 3 lines (the first \n ends a text line and the second one creates a blank line). I'm sure my objection was based on a real life scenario at some point but I have no idea what at this point.Animatism
If your text only takes '\n' for new-line characters(e.g. a <textarea>'s value), you can consider using TEXT.match(/^/mg).length.Agraphia
you answer is incorrect consider case "\n\n". There is only two lines. But your code outputs 3. Which is not correct.Aston
@Aston You miscounted, "\n\n" has 3 lines, each containing an empty string. Every string (even the empty string) has 1 line, then each \n adds another line. It's really the same thing as looking at "1\n2\n3", which is the numbers 1, 2 and 3, each on their own line.Disaccord
H
80

Another short, potentially more performant than split, solution is:

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

to avoid possible errors, it can help to convert it explicitly into a string ( https://mcmap.net/q/182666/-javascript-error-quot-val-match-is-not-a-function-quot ) :

const lines = (String(str).match(/\n/g) || '').length + 1
Hasidism answered 6/5, 2017 at 12:46 Comment(6)
this is way better solutionTraject
like this solution, small improvement: the \r? isn't actually doing anything, (str.match(/\n/g) || '').length produces the same result, doesn't it?Fedora
Better solution, as split function creates new array which is heavy than this solution.Corbeil
both methods create a new array … str.match return an Array so does the split too … The split method return an Array of strings, but str.match return an Array of objects. I think an object take more space in memory than string …Sideslip
clear winner in benchmarkFlo
@Flo for me split performs better :) it's surprising how bad for loop is.Whiffen
D
11

To split using a regex use /.../

lines = str.split(/\r\n|\r|\n/); 
Demogorgon answered 13/12, 2011 at 11:56 Comment(1)
Same rule but quite shorter /\r?\n/Lotty
A
11

Hmm yeah... what you're doing is absolutely wrong. When you say str.split("\r\n|\r|\n") it will try to find the exact string "\r\n|\r|\n". That's where you're wrong. There's no such occurance in the whole string. What you really want is what David Hedlund suggested:

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

The reason is that the split method doesn't convert strings into regular expressions in JavaScript. If you want to use a regexp, use a regexp.

Article answered 13/12, 2011 at 12:1 Comment(0)
S
8

I made a performance test comparing split with regex, with a string and doing it with a for loop.

It seems that the for loop is the fastest.

NOTE: this code 'as is' is not useful for windows nor macos endline, but should be ok to compare performance.

Split with string:

split('\n').length;

Split with regex:

split(/\n/).length;

Split using for:

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

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

Strang answered 13/4, 2015 at 14:26 Comment(1)
funny, my benchmark says that for-loop is slowestFlo
L
3

There are three options:

Using jQuery (download from jQuery website) - jquery.com

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

Using Regex

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

Or, a recreation of a for each loop

var length = 0;
for(var i = 0; i < str.length; ++i){
    if(str[i] == '\n') {
        length++;
    }
}
return length;
Lansquenet answered 12/12, 2013 at 18:15 Comment(1)
This is not a true comment. There are not just three options.Dotson
C
2

Better solution, as str.split("\n") function creates new array of strings split by "\n" which is heavier than str.match(/\n\g). str.match(/\n\g) creates array of matching elements only. Which is "\n" in our case.

var totalLines = (str.match(/\n/g) || '').length + 1;
Corbeil answered 13/5, 2019 at 6:57 Comment(0)
F
2

Another solution for this problem using the spread operator and no regular expressions would be:

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);
Forespent answered 2/3, 2021 at 15:43 Comment(0)
V
1

Here is the working sample fiddle

Just remove additional \r\n and "|" from your reg ex.

Vitrine answered 13/12, 2011 at 11:56 Comment(0)
H
1
 <script type="text/javascript">
      var multilinestr = `
        line 1
        line 2
        line 3
        line 4
        line 5
        line 6`;
      totallines = multilinestr.split("\n");
lines = str.split("\n"); 
console.log(lines.length);
</script>

thats works in my case

Hupp answered 5/8, 2019 at 11:10 Comment(0)
D
1

I was testing out the speed of the functions, and I found consistently that this solution that I had written was much faster than matching. We check the new length of the string as compared to the previous length.

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")
Dotson answered 10/2, 2021 at 19:1 Comment(0)
S
0

I've combined a few of the other answers into a performance test at https://jsperf.app/lenavi/4:

.match(/\n/g) from @ngryman and @hashed_name:

(text.match(/\n/g) || '').length + 1

.reduce(...) from @gil.fernandes:

[...text].reduce((a, c) => a + (c === '\n'), 1)

.split(/\r\n|\r|\n/) from @Joe, @Pavan, @Aadit M Shah, and @David Hedlund:

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

.split(/\n/) for when you know the line ending, by @jperelli:

text.split(/\n/).length

.split('\n') which is the same as above, but using a string instead of a regex, by @Krishna Jangid, @jperelli, @Joe, and @Pavan:

text.split('\n').length

Here are the results on my machine with Chrome 123.0.6312.105:

  1. .split('\n') is the fastest by far.
  2. .split(/\n/) and .match(/\n/g) are virtually tied for second at 59% slower.
  3. .split(/\r\n|\r|\n/) is about 87% slower.
  4. .reduce(...) is 99% slower. Not even in the same ballpark.

The results are different (on my machine) with Firefox 123.0.1:

  1. .split(/\n/) and .match(/\n/g) are virtually tied for first. I'm a little surprised that .split() did this well. Perhaps the SpiderMonkey JavaScript engine is special-casing this particular call.
  2. .split('\n') is ~50% slower. I'm very surprised by the difference here between using a one-character string and a one-character regex.
  3. .split(/\r\n|\r|\n/) is about 66% slower, or 1/3 the speed, which makes sense because it is doing up to 3x the comparisons.
  4. .reduce(...) is 99% slower (again).

Node uses the same JavaScript engine (V8) as Chrome.

Safeconduct answered 12/4 at 16:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.