124

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?

5
  • 3
    @BookOfZeus "\n" and "\r" are handled by his regexp. "\n\r" is plain wrong. Commented Dec 13, 2011 at 11:54
  • oh i see it, you are right my bad Commented Dec 13, 2011 at 12:01
  • I've answered a related question, "What's the fastest way to test for a minimum number of lines or tokens?" stackoverflow.com/questions/39554154/… Commented Sep 18, 2016 at 4:38
  • @bezmax "\n\r" is necessary for pasted text. Commented Oct 14, 2019 at 9:11
  • @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. Commented Oct 15, 2019 at 13:39

11 Answers 11

197

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/

Sign up to request clarification or add additional context in comments.

6 Comments

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.
@SimplGy 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.
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.
If your text only takes '\n' for new-line characters(e.g. a <textarea>'s value), you can consider using TEXT.match(/^/mg).length.
@Khamidulla 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.
|
84

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://stackoverflow.com/a/5196710/2891692 ) :

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

6 Comments

this is way better solution
like this solution, small improvement: the \r? isn't actually doing anything, (str.match(/\n/g) || '').length produces the same result, doesn't it?
Better solution, as split function creates new array which is heavy than this solution.
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 …
@milahu for me split performs better :) it's surprising how bad for loop is.
|
11

To split using a regex use /.../

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

1 Comment

Same rule but quite shorter /\r?\n/
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.

Comments

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

1 Comment

funny, my benchmark says that for-loop is slowest
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;

1 Comment

This is not a true comment. There are not just three options.
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;

Comments

1

Here is the working sample fiddle

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

Comments

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

Comments

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")

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.