Skip to Content
RowSlice

Guide

How to Count the Rows in a CSV File

Published · Updated

Short Answer

A line count is not a row count. Take one off for the header, and remember that a value with a line break inside quotes spans two lines but is only one row, so on a file with free-text fields only a real CSV parser gives the true number.

  1. Open RowSlice’s CSV Analyzer and choose your CSV. Nothing is uploaded.
  2. Press Analyze File.
  3. Read the data row count: the header is excluded and completely blank lines are skipped.
  4. Compare the line count with the record count. If they agree, nothing in the file has a line break inside a value, and wc -l would have been safe.
Analyze a CSV File

For a quick line count use wc -l on macOS or Linux, find /c /v "" in the Windows Command Prompt, or [System.IO.File]::ReadLines in PowerShell; for the true row count outside the browser, use DuckDB or Python’s csv module.

Somebody asks how many records are in the export and you get three answers: the text editor says 2,000,001, a colleague’s command says 2,000,000, and the import tool that rejected the file says 1,999,873. None of them is lying. They are counting different things.

A Line Count Is Not a Row Count

Almost every answer you will find online counts lines, and a CSV row is not the same as a line. Three things pull the numbers apart.

The header is a line. If the first line is id,name,city, your data row count is one less than the line count — easy to remember, easy to forget when someone quotes you a number.

A value can contain line breaks. RFC 4180, the closest thing CSV has to a specification, says fields “containing line breaks (CRLF), double quotes, and commas should be enclosed in double-quotes”. So this is two data rows, not three:

id,note
1,"line one
line two"
2,ok

A line counter reports 4. A real CSV parser reports 3 records, or 2 data rows. Python’s documentation is blunt about the distinction: the csv reader’s line_num is “the number of lines read from the source iterator. This is not the same as the number of records returned, as records can span multiple lines.” Any export with free-text fields — addresses, product descriptions, ticket bodies — is likely to contain these.

The last line may have no line break. RFC 4180 says “the last record in the file may or may not have an ending line break”, and tools disagree about what to do when it does not. That is the source of most off-by-one arguments.

Counting Lines Quickly (and Knowing What You Got)

Line counts are still useful: they are fast, and on a file with no line breaks inside values they are the row count plus one. Just label the number honestly when you pass it on.

macOS and Linux

wc -l file.csv is the standard answer, but read the specification carefully: the POSIX definition of -l is to “write to the standard output the number of <newline> characters in each input file”. It counts line breaks, not lines. A three-line file whose last line has no trailing newline reports 2. So: data rows ≈ wc -l − 1, plus 1 again if there is no final newline.

Windows PowerShell

The usual advice, (Get-Content file.csv | Measure-Object -Line).Lines, is correct but slow, because Get-Content emits every line as a separate object through the pipeline. Reading the file with .NET instead is several times faster and still streams, so memory use stays flat. Measured here on a 112 MB file of 2,000,001 lines in Windows PowerShell 5.1:

CommandResultTime
(Get-Content file.csv | Measure-Object -Line).Lines2,000,00124.3 s
([System.IO.File]::ReadLines('C:\data\file.csv') | Measure-Object -Line).Lines2,000,00110.5 s
((Get-Content file.csv -Raw) | Measure-Object -Line).Lines2,000,0011.7 s
(Get-Content file.csv -ReadCount 1000 | Measure-Object -Line).Lines2,001 — wrong1.1 s

Two warnings about that table. The -Raw version is fastest because it reads the file as one enormous string, so the whole file has to fit in memory — fine at 112 MB, not at 4 GB. And the -ReadCount 1000 trick, which circulates as a speed tip, is simply wrong: Get-Content then hands the pipeline batches of 1,000 lines, and you get the number of batches. The seconds are from one Windows 11 laptop and will differ on yours; it is the ranking, and the wrong answer in the last row, that travel.

[System.IO.File]::ReadLines is a .NET call, so give it a full path — it resolves relative paths against the process working directory, not the folder PowerShell is showing you. Unlike wc -l, it does count a final line that has no line break.

Text Editors

Notepad++ shows the count in its status bar, which displays “the length of the file (in bytes, not characters …) and the number of lines in the file”. VS Code’s status bar shows where the cursor is rather than a total, so press Ctrl+End and read the line number instead. Both have a ceiling: past a certain size an editor stops treating a file as editable text and disables syntax highlighting and wrapping, so if the window goes plain, trust the command line rather than the editor.

Windows Command Prompt: find /c /v ""

Command Prompt has its own line counter: find /c /v "" file.csv. find treats no line as containing the empty string, so /v, which keeps the lines that do not contain the search text, keeps every line, and /c prints how many there were instead of the lines themselves. Given a file name, it prints the count after a blank line and a row of dashes, with the name in capitals: ---------- FILE.CSV: 2000001. Given the text through a pipe or a redirect — type file.csv | find /c /v "" or find /c /v "" < file.csv — it prints the number alone, which is the form a script wants.

It is a line count, with the same catch as wc -l and ReadLines: the header is one of the lines, and every line break inside a quoted value adds another, so on the four-line example above it reports 4 for 2 data rows. Like ReadLines, it counts a final line that has no line break, and blank lines count too. Type it into Command Prompt, not PowerShell: Windows PowerShell 5.1 does not pass the empty "" on, and find answers FIND: Parameter format not correct.

Measured on Windows 11, on a file with the same size and line count as the one in the PowerShell table — 112 MB, 2,000,001 lines, Windows line endings — taking the median of seven runs:

CommandResultTime
find /c /v "" file.csv2,000,0010.5 s
find /c /v "" < file.csv2,000,0010.5 s
type file.csv | find /c /v ""2,000,0012.3 s

In the same session, on this file, the ReadLines command from the PowerShell table took more than ten times as long and the -Raw one more than twice as long, so at this size find is the quickest Windows line count on this page. On very large files it breaks, and not always visibly. Given a file name, it counted a file of exactly 3 GB (3,221,225,472 bytes) correctly; one byte longer, it printed Unable to read file and no count. Redirected input got past that limit and then failed worse: on a 4.2 GB file of 75,000,001 lines, find /c /v "" < file.csv printed 3,608,131 with no error — the lines in the first 203 MB, which is the amount by which the file exceeds 4 GB. Piped through type, the same file came back right, 75,000,001, in 133 s. Above 3 GB, use the pipe, or a tool from the next section.

Getting a True Row Count

When the file has free-text fields, or when the number is going into a reconciliation, count records rather than lines.

Stream It in the Browser

All the tools on this site parse CSV properly — quoted line breaks included — and count as they read. While a job runs, the progress line shows the bytes done and a running row count; when it finishes, the summary gives the total. The cleanest number comes from RowSlice’s CSV Analyzer, because it reports all three counts at once instead of one of them: physical lines, CSV records, and data rows with the header excluded and completely blank lines skipped rather than counted. Reading them together is what settles the argument — when the line count and the record count agree, nothing in the file has a line break inside a value and wc -l would have been safe after all. Nothing is uploaded; the file is read on your machine.

One thing to know before running it on something enormous: the analyzer counts exact duplicate rows as well, which is the one part of the run that holds anything sizeable in memory, and it stops with a message past about 33 million different rows. Untick Count exact duplicate rows and that ceiling goes with it.

The usual caveat about disk space does not apply here. The analyzer writes only its column report, a few kilobytes however large the input. It is the tools that produce a copy of your rows — Split CSV into Multiple Files, Merge CSV Files, Remove Duplicates from CSV — that write that copy into the browser’s private on-disk storage as they go, and for those you need roughly as much free space as the file takes.

DuckDB

One line, no import step, and a proper CSV parser with delimiter and quote detection:

SELECT count(*) FROM 'orders.csv';

The best option on multi-gigabyte files: nothing has to fit in memory.

Power Query

If the file is already loaded in Excel’s Power Query editor, there is a Count Rows command on the Transform tab that replaces the table with a single number (it applies the documented Table.RowCount function). Do not read the count off the data-profiling pane instead: Power Query “profiles data over the first 1,000 rows” by default, and you have to switch to Column profiling based on entire data set in the bottom-left corner of the editor to get whole-file figures.

Python

Three lines, and it handles embedded line breaks because the csv module does:

import csv
f = open('orders.csv', newline='', encoding='latin-1')
print(sum(1 for _ in csv.reader(f)) - 1)

The - 1 drops the header. Passing newline='' is not optional: the documentation notes that without it, “newlines embedded inside quoted fields will not be interpreted correctly”.

The encoding='latin-1' is what makes the file open on any computer. Without it, Python falls back on a default encoding and can stop with a UnicodeDecodeError: on Windows in Python 3.14 and earlier, for some UTF-8 files (a name with a Polish Ł is enough), and on a Mac or Linux, or on any computer in Python 3.15 and later, for Windows-1252 files with accents. Latin-1 gives every byte a character, and the commas, quotes and line breaks that mark rows are the same bytes in UTF-8 and Windows-1252, so the count is right for either. The exception is UTF-16, and a quiet one: the script prints a wrong number instead of an error, so for a UTF-16 file write encoding='utf-16' instead. If you are not sure which encoding you have, how to tell UTF-8, UTF-16 and Windows-1252 apart walks through it.

Why Excel’s Row Number Can Mislead You

Pressing Ctrl+End in a worksheet shows how many rows you have — of the rows Excel loaded, which is not the same thing. A worksheet stops at 1,048,576, so a row indicator reading exactly 1048576 is a truncated file rather than a remarkable coincidence. Excel’s row limit explained covers what happens to the rest, and why saving at that moment is the expensive mistake.

Counting Rows per Category, Not Just in Total

Usually the interesting question is not “how many rows” but “how many per country, per store, per status”. Running split by column value answers it as a side effect: the results screen lists one output file per distinct value with its row count, largest first, before you download anything. Pick the column, run it, read the list, close the tab. It refuses columns with more than 10,000 distinct values, which is the point at which you want a database instead.

For duplicate-versus-unique counts, the duplicate remover’s summary gives both, which is usually all you need before an import. In SQL the equivalent is SELECT country, count(*) FROM 'orders.csv' GROUP BY country ORDER BY 2 DESC;

Which Method at Which Size

File sizeUseAvoid
Under 10 MBAny editor, or a spreadsheet—
10–100 MBwc -l, find /c /v "" in Command Prompt, ReadLines in PowerShell, or a browser tool for the true countNotepad
100 MB – 2 GBA streaming tool or DuckDB; find /c /v "" for a line countGet-Content piped line by line; -Raw
Over 2 GBDuckDB, or the CSV analyzer in the browserAnything that loads the file into memory; find past 3 GB unless piped from type

If the count was only a step towards making the file usable, the next move is splitting the large CSV into files that Excel can open — or, if the total came out wrong because several exports were being added up, merging files whose columns differ. And opening a CSV that is too large for Excel covers what to do with the rows once you know how many there are. The same line-versus-row difference decides whether a command-line split is safe: splitting a CSV with PowerShell, Bash or Python shows a split that cuts a row in half, and one that does not.

FAQ

Frequently Asked Questions

Does wc -l give me the number of rows in my CSV?

Not exactly. POSIX defines wc -l as counting newline characters, so it reports one fewer than you expect when the last line has no trailing newline, it counts the header as a line, and it counts every line break inside a quoted value as an extra line. As a rule: data rows are roughly wc -l minus 1, plus 1 again if there is no final newline.

Does a row count include the header row?

That depends on who is counting, which is why one file produces two answers. Line counters include the header; import tools and the summaries on this site report data rows and exclude it. Neither is wrong, and the header is only one of the reasons two counts of the same file disagree. When you hand a number to someone else, label it — “lines” or “data rows” — before they compare it with theirs.

Why does Get-Content -ReadCount 1000 report 2,001 rows?

Because you counted batches, not lines. With -ReadCount 1000, Get-Content hands the pipeline arrays of 1,000 lines each and Measure-Object -Line counts the objects it receives, so 2,000,001 lines arrive as 2,001 batches. The tip circulates as a speed trick and is simply wrong. Use [System.IO.File]::ReadLines() with a full path instead.

Why has VS Code stopped highlighting and wrapping my CSV?

It has crossed the size at which an editor stops treating a file as editable text, so syntax highlighting and wrapping switch off to keep the window responsive. Nothing has been lost; every line is still there. VS Code’s status bar shows where the cursor is rather than a total, so press Ctrl+End and read the line number. Once the window goes plain, trust the command line over the editor.

Do blank lines count as rows?

Not in the counts the tools here report — they skip completely blank lines rather than counting them, which is one more reason a line count and a row count disagree. A line of empty fields such as ,,, is a different thing: that is a real row that happens to hold no values, and it is counted. A line counter counts both.

Do I need free disk space just to count the rows?

Not for the count itself. The CSV analyzer reads the file and writes only its column report, a few kilobytes however large the input. The tools that exist to produce files — Split, Merge, Remove Duplicates, the encoding converter — write their output to your disk as they read, and those need roughly as much free space as the file takes, even when all you wanted was a number. Outside the browser, SELECT count(*) FROM 'orders.csv'; in DuckDB also reads the file without writing anything.

Guides

See All 20 CSV Guides