117

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?

Gian Arauz
  • 423
  • 1
  • 7
  • 14
Itzik984
  • 15,968
  • 28
  • 69
  • 107
  • 3
    @BookOfZeus "\n" and "\r" are handled by his regexp. "\n\r" is plain wrong. – bezmax Dec 13 '11 at 11:54
  • oh i see it, you are right my bad – Book Of Zeus Dec 13 '11 at 12:01
  • I've answered a related question, "What's the fastest way to test for a minimum number of lines or tokens?" http://stackoverflow.com/questions/39554154/fastest-way-to-test-for-a-minimum-number-of-lines-or-tokens – Joe Lapp Sep 18 '16 at 04:38
  • @bezmax "\n\r" is necessary for pasted text. – Supreme Dolphin Oct 14 '19 at 09:11
  • @SupremeDolphin No it is not, at least not for the example given. See https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_message : "The request line and other header fields must each end with ", that is `\r\n`. – bezmax Oct 15 '19 at 13:39

11 Answers11

186

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/

Pavan
  • 4,209
  • 1
  • 22
  • 25
  • 2
    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 May 22 '14 at 18:27
  • 7
    @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. – Jools Aug 16 '16 at 07:16
  • 1
    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. – SimplGy Aug 16 '16 at 18:44
  • 4
    If your text only takes '\n' for new-line characters(e.g. a ` – Константин Ван Nov 19 '16 at 07:09
  • you answer is incorrect consider case "\n\n". There is only two lines. But your code outputs 3. Which is not correct. – Khamidulla Jan 23 '19 at 03:20
  • 1
    @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. – scenia Jan 14 '21 at 17:35
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://stackoverflow.com/a/5196710/2891692 ) :

const lines = (String(str).match(/\n/g) || '').length + 1
SL5net
  • 2,282
  • 4
  • 28
  • 44
ngryman
  • 7,112
  • 2
  • 26
  • 23
  • this is way better solution – asmmahmud May 31 '18 at 10:41
  • 3
    like this solution, small improvement: the `\r?` isn't actually doing anything, `(str.match(/\n/g) || '').length` produces the same result, doesn't it? – Samuel Kirschner Jun 13 '18 at 15:09
  • 1
    Better solution, as split function creates new array which is heavy than this solution. – hashed_name May 13 '19 at 06:49
  • 2
    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 … – Bruno Desprez Aug 01 '19 at 09:24
  • clear winner in [benchmark](http://jsbench.github.io/#4cc806b4507ae063efc81900cdfb9b02) – milahu Oct 05 '20 at 15:13
  • 1
    @milahu for me split performs better :) it's surprising how bad for loop is. – TWiStErRob Jun 18 '22 at 18:23
11

To split using a regex use /.../

lines = str.split(/\r\n|\r|\n/); 
David Hedlund
  • 128,221
  • 31
  • 203
  • 222
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.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
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

aleclarson
  • 18,087
  • 14
  • 64
  • 91
jperelli
  • 6,988
  • 5
  • 50
  • 85
  • funny, my [benchmark](http://jsbench.github.io/#4cc806b4507ae063efc81900cdfb9b02) says that for-loop is slowest – milahu Oct 05 '20 at 15:14
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;
alexn
  • 57,867
  • 14
  • 111
  • 145
Joe
  • 528
  • 5
  • 19
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;
hashed_name
  • 553
  • 6
  • 21
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);
gil.fernandes
  • 12,978
  • 5
  • 63
  • 76
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

Krishna Jangid
  • 4,961
  • 5
  • 27
  • 33
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")
Chris - Jr
  • 398
  • 4
  • 11
1

Here is the working sample fiddle

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

Sandeep G B
  • 3,957
  • 4
  • 26
  • 43