Table of Contents
C# Deep Dive
How to Count Lines in a File Using C# (And the Int32 Overflow Trap Nobody Warns You About)
Count lines in a file using C# — File.ReadAllLines, File.ReadLines, StreamReader, and async methods. Includes the Int32 overflow trap, GC pressure benchmarks, and .NET 6+ best practices.
Three ways to count lines in C#:
- One loads the entire file into memory.
- One streams the file but can overflow at 2.1 billion lines.
- One is the right default for production.
Here is the decision in one table:
| Method | Memory | Large files | Main risk |
|---|---|---|---|
File.ReadAllLines().Length | high | no | OOM |
File.ReadLines().Count() | low | risky | Int32 overflow |
File.ReadLines().LongCount() | low | better | one string per line |
StreamReader loop | low | yes | more code |
Span<byte> byte scan | lowest | yes | raw byte semantics |
This guide covers c# count lines methods from simple one-liners to .NET 6+ byte scanning. It also explains the file readlines count overflow trap: Count() returns int, so a huge log can throw even when streaming memory use is low. If your search was "c# count lines in file," the short answer is streaming plus LongCount().
If you only need a quick result without a .NET project, use the Line Counter tool. If you are writing C# code for logs, CSVs, uploads, or Azure services, keep reading.
Quick Method Guide
| Scenario | Use this |
|---|---|
| Small known file | read-all method with .Length |
| Concise streaming count | streaming method with .LongCount() |
| Production large file | StreamReader loop with long counter |
| ASP.NET Core | async StreamReader.ReadLineAsync(...) |
| Maximum throughput | Span<byte> or pooled byte buffer scan |
| More than 2.1 billion lines possible | never use LINQ Count() |
For most count lines c# code, start with the streaming method plus LongCount(). For c# count lines large file work in services, use a StreamReader count lines loop so you control buffers, async behavior, cancellation, and the counter type.
Method 1: File.ReadAllLines - Simple but Memory-Hungry
The shortest beginner answer is:
using System.IO;
int lineCount = File.ReadAllLines("data.txt").Length;
With basic validation:
using System.IO;
public static int CountLinesSmallFile(string filePath)
{
if (!File.Exists(filePath))
throw new FileNotFoundException($"File not found: {filePath}");
return File.ReadAllLines(filePath).Length;
}
Microsoft documents File.ReadAllLines as opening a file, reading all lines into a string array, and closing the file. That array is the memory trap.
| File size | Use File.ReadAllLines? | Reason |
|---|---|---|
| Under 50MB | usually fine | simple and readable |
| 50MB to 500MB | be careful | all lines are materialized |
| Over 500MB | avoid | OOM and GC pressure risk |
The read-all method is useful for tests, small config files, and quick local tools. It is not a safe general answer for c# count lines large file scenarios.
Method 2: File.ReadLines - Streaming with an Overflow Trap
Most Stack Overflow-style answers look like this:
using System.IO;
using System.Linq;
int lineCount = File.ReadLines("data.txt").Count();
The streaming method is better than the read-all method because it does not materialize the entire file first. Microsoft documents it as returning an enumerable collection whose enumeration can begin before the whole collection is returned.
string[] allLines = File.ReadAllLines("data.txt");
IEnumerable<string> lazyLines = File.ReadLines("data.txt");
The streaming part is good. The Count() part is the trap.
The Int32 overflow trap
LINQ Count() returns int.
public static int Count<TSource>(this IEnumerable<TSource> source)
If the sequence has more than Int32.MaxValue items, Count() throws OverflowException. That means file readlines count overflow can happen even though the file is streamed and memory stays low.
// Dangerous on files with more than 2,147,483,647 lines
int count = File.ReadLines("huge.log").Count();
The fix is one word:
using System.IO;
using System.Linq;
long lineCount = File.ReadLines("huge.log").LongCount();
LongCount() returns long, so it is the safe concise method.
public static long CountLinesStreaming(string filePath)
{
if (!File.Exists(filePath))
throw new FileNotFoundException($"File not found: {filePath}");
return File.ReadLines(filePath).LongCount();
}
Use LongCount c# style whenever the file is not tightly bounded. The cost is effectively the same as Count(); the return type is the difference that matters.
Streaming still allocates strings
The streaming method reads lazily, but every line is still a string. For millions or billions of rows, that means many short-lived string allocations and Gen0 collections.
That is acceptable for many business files. It is not the fastest possible way to count newlines.
Use streaming plus LongCount() when:
- you want simple c# count lines code
- the file might be large but not extreme
- you need text decoding and line semantics
- you do not need async cancellation
Move to StreamReader when this runs in production hot paths.
Method 3: StreamReader Manual Loop - The Production Choice
A manual StreamReader count lines loop avoids Int32 overflow because you choose long.
using System.IO;
public static long CountLinesWithStreamReader(string filePath)
{
long count = 0;
using var reader = new StreamReader(filePath);
while (reader.ReadLine() != null)
{
count++;
}
return count;
}
This is the baseline production method.
Add an explicit buffer and SequentialScan
Default stream buffers are deliberately conservative. In .NET source, StreamReader has a small internal character buffer, and FileStream defaults have historically been small enough that an explicit large buffer can help large sequential reads.
Use an explicit buffer for c# count lines large file code:
using System.IO;
public static long CountLinesOptimized(string filePath)
{
const int bufferSize = 1024 * 1024;
long count = 0;
using var fileStream = new FileStream(
filePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
bufferSize,
FileOptions.SequentialScan);
using var reader = new StreamReader(
fileStream,
bufferSize: bufferSize);
while (reader.ReadLine() != null)
count++;
return count;
}
FileOptions.SequentialScan tells the operating system that the file will be read from beginning to end. That can improve read-ahead behavior and cache decisions.
This StreamReader count lines version is a strong default when:
- files can be hundreds of MB or GB
- you need a
longcount - you need predictable memory use
- you want text decoding and normal line-ending handling
For related JVM patterns, see Java line counting.
Async version for ASP.NET Core
Synchronous file I/O can block ASP.NET Core request threads. For c# async count lines in web apps, use async file I/O and pass the request cancellation token.
using System.IO;
using System.Threading;
using System.Threading.Tasks;
public static async Task<long> CountLinesAsync(
string filePath,
CancellationToken cancellationToken = default)
{
const int bufferSize = 1024 * 1024;
await using var fileStream = new FileStream(
filePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
bufferSize,
FileOptions.Asynchronous | FileOptions.SequentialScan);
using var reader = new StreamReader(
fileStream,
bufferSize: bufferSize);
long count = 0;
while (await reader.ReadLineAsync(cancellationToken) != null)
{
count++;
}
return count;
}
ASP.NET Core example:
[HttpGet("line-count")]
public async Task<IActionResult> GetLineCount(string filename)
{
var count = await CountLinesAsync(filename, HttpContext.RequestAborted);
return Ok(new { lines = count });
}
Microsoft documents the ReadLineAsync(CancellationToken) overload as returning ValueTask<string?> and returning null at end of stream. Use it on .NET 6+ code paths when cancellation matters.
Method 4: Span<T> Byte Scanning - Zero String Allocation
If you only need physical newline counts, scan bytes.
This avoids string allocation entirely.
using System;
using System.IO;
public static long CountLinesFast(string filePath)
{
const int bufferSize = 64 * 1024;
Span<byte> buffer = stackalloc byte[bufferSize];
long count = 0;
bool sawAnyByte = false;
byte lastByte = (byte)'\n';
using var fileStream = new FileStream(
filePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
bufferSize,
FileOptions.SequentialScan);
int bytesRead;
while ((bytesRead = fileStream.Read(buffer)) > 0)
{
sawAnyByte = true;
var chunk = buffer[..bytesRead];
#if NET8_0_OR_GREATER
count += chunk.Count((byte)'\n');
#else
count += CountNewlines(chunk);
#endif
lastByte = chunk[^1];
}
if (sawAnyByte && lastByte != (byte)'\n')
count++;
return count;
}
private static int CountNewlines(ReadOnlySpan<byte> span)
{
int count = 0;
foreach (var b in span)
{
if (b == (byte)'\n')
count++;
}
return count;
}
This is the Span<T> count lines .NET method for high-throughput tools. It is fastest because it avoids:
stringallocation- text decoding
ReadLine()logic- LINQ enumeration overhead
Stack allocation is fast, but keep it modest. A 64KB stackalloc buffer is a reasonable upper bound in many CLI tools. For library code or unknown hosting environments, prefer ArrayPool<byte> so you do not consume too much stack.
Pooled variant:
using System.Buffers;
using System.IO;
public static long CountLinesFastPooled(string filePath)
{
const int bufferSize = 1024 * 1024;
byte[] rented = ArrayPool<byte>.Shared.Rent(bufferSize);
try
{
var buffer = rented.AsSpan(0, bufferSize);
long count = 0;
bool sawAnyByte = false;
byte lastByte = (byte)'\n';
using var stream = new FileStream(
filePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
bufferSize,
FileOptions.SequentialScan);
int bytesRead;
while ((bytesRead = stream.Read(buffer)) > 0)
{
sawAnyByte = true;
var chunk = buffer[..bytesRead];
count += CountNewlines(chunk);
lastByte = chunk[^1];
}
if (sawAnyByte && lastByte != (byte)'\n')
count++;
return count;
}
finally
{
ArrayPool<byte>.Shared.Return(rented);
}
}
Use byte scanning when invalid text encoding should not matter and line breaks are all you need.
Benchmark: All Methods Compared
BenchmarkDotNet shape:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.IO;
using System.Linq;
[MemoryDiagnoser]
public class LineCountBenchmarks
{
private const string FilePath = "testdata/large.txt";
[Benchmark(Baseline = true)]
public int ReadAllLines()
=> File.ReadAllLines(FilePath).Length;
[Benchmark]
public long ReadLinesLongCount()
=> File.ReadLines(FilePath).LongCount();
[Benchmark]
public long StreamReaderDefault()
=> CountLinesWithStreamReader(FilePath);
[Benchmark]
public long StreamReaderLargeBuffer()
=> CountLinesOptimized(FilePath);
[Benchmark]
public long SpanByteScan()
=> CountLinesFast(FilePath);
}
BenchmarkRunner.Run<LineCountBenchmarks>();
Directional results for a 500MB newline-delimited file on SSD storage, .NET 8:
| Method | Time shape | Allocation | Main issue |
|---|---|---|---|
File.ReadAllLines().Length | slow | very high | reads whole file |
File.ReadLines().Count() | medium | low live memory | file readlines count overflow |
File.ReadLines().LongCount() | medium | low live memory | string per line |
StreamReader default | medium | low | conservative buffers |
StreamReader large buffer | faster | low | more code |
Span<T> byte scan | fastest | near zero | byte semantics |
The exact numbers depend on storage, CPU, line length, encoding, OS file cache, and .NET version. The stable ranking is what matters:
File.ReadAllLinesis simplest and most memory-hungry.LongCount c#fixes theCount()overflow problem.StreamReader count linesloops are the production default.Span<T> count lines .NETbyte scans are fastest when text decoding is unnecessary.
.NET Version Compatibility
| Method | .NET Framework | .NET Core | .NET 5 | .NET 6+ |
|---|---|---|---|---|
File.ReadAllLines | yes | yes | yes | yes |
File.ReadLines | 4.0+ | yes | yes | yes |
Enumerable.LongCount | 3.5+ | yes | yes | yes |
StreamReader | yes | yes | yes | yes |
ReadLineAsync(CancellationToken) | no | no | no | yes |
Span<T> byte APIs | no | 2.1+ | yes | yes |
FileOptions.SequentialScan | yes | yes | yes | yes |
If you target .NET Framework 4.8, use synchronous StreamReader or File.ReadLines().LongCount(). If you target .NET 6+, use c# async count lines methods in ASP.NET Core and byte scanning for high-throughput CLI tools.
A Production-Ready Line Counter Class
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public static class LineCounter
{
private const long SmallFileThreshold = 50L * 1024 * 1024;
private const int BufferSize = 1024 * 1024;
public static long Count(string filePath)
{
var fileInfo = new FileInfo(filePath);
if (!fileInfo.Exists)
throw new FileNotFoundException($"File not found: {filePath}");
if (fileInfo.Length < SmallFileThreshold)
return File.ReadLines(filePath).LongCount();
return CountWithStreamReader(filePath);
}
public static async Task<long> CountAsync(
string filePath,
CancellationToken cancellationToken = default)
{
if (!File.Exists(filePath))
throw new FileNotFoundException($"File not found: {filePath}");
await using var fileStream = new FileStream(
filePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
BufferSize,
FileOptions.Asynchronous | FileOptions.SequentialScan);
using var reader = new StreamReader(fileStream, bufferSize: BufferSize);
long count = 0;
while (await reader.ReadLineAsync(cancellationToken) != null)
count++;
return count;
}
public static long CountNonEmpty(string filePath)
=> File.ReadLines(filePath)
.LongCount(line => !string.IsNullOrWhiteSpace(line));
private static long CountWithStreamReader(string filePath)
{
long count = 0;
using var fileStream = new FileStream(
filePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
BufferSize,
FileOptions.SequentialScan);
using var reader = new StreamReader(fileStream, bufferSize: BufferSize);
while (reader.ReadLine() != null)
count++;
return count;
}
}
Usage:
var count = LineCounter.Count("data.txt");
var countAsync = await LineCounter.CountAsync("data.txt", cancellationToken);
var nonEmpty = LineCounter.CountNonEmpty("config.txt");
Production checklist:
- Use
LongCount()instead ofCount()for unbounded files. - Use
StreamReader, notFile.ReadAllLines, for large files. - Set explicit buffers for large sequential reads.
- Add
FileOptions.SequentialScan. - Use async file I/O in ASP.NET Core.
- Pass
CancellationToken. - Dispose streams with
usingorawait using. - Decide whether byte semantics or text semantics are required.
Special Scenarios
stdin
using System;
using System.IO;
long count = 0;
using var reader = new StreamReader(Console.OpenStandardInput());
while (reader.ReadLine() != null)
count++;
Console.WriteLine(count);
strings
Use StringReader when you want the same line behavior as ReadLine():
using System.IO;
public static long CountLinesInString(string text)
{
if (string.IsNullOrEmpty(text))
return 0;
long count = 0;
using var reader = new StringReader(text);
while (reader.ReadLine() != null)
count++;
return count;
}
Raw newline count:
public static long CountLinesInStringFast(string text)
{
if (string.IsNullOrEmpty(text))
return 0;
long count = 0;
foreach (char c in text)
{
if (c == '\n')
count++;
}
if (text[^1] != '\n')
count++;
return count;
}
Windows line endings
StreamReader.ReadLine() and File.ReadLines return strings without the terminating carriage return or line feed. Windows CRLF files need no special handling.
In byte scanning, counting \n is still correct for CRLF because each CRLF line ending includes one LF byte. Lone old-Mac \r files need separate handling if they matter to your domain.
Which C# Method Should You Use?
Use this c# count lines decision tree when you need the fastest safe choice:
Need to count lines in C#?
|
+-- Small known file?
| +-- streaming method with LongCount()
|
+-- Large text file?
| +-- StreamReader + explicit buffer + SequentialScan
|
+-- ASP.NET Core request path?
| +-- StreamReader.ReadLineAsync(CancellationToken)
|
+-- Raw speed and newline bytes only?
| +-- Span<byte> byte scan or ArrayPool<byte>
|
+-- More than 2.1 billion lines possible?
+-- LongCount or manual long counter, never Count()
The safest short answer is the streaming method with LongCount(). The safest production answer is a StreamReader count lines loop. The fastest answer is byte scanning.
FAQ
How do I count lines in a file in C#?
Use the streaming method with LongCount() for a concise count. Use StreamReader with a long counter for production code.
What is the difference between ReadAllLines and ReadLines?
The read-all method reads every line into a string[]. The streaming method returns a lazy IEnumerable<string> and reads as you enumerate.
Why does File.ReadLines().Count() throw OverflowException?
LINQ Count() returns int. If the sequence has more than Int32.MaxValue elements, it throws. Use LongCount() or a manual long counter to fix file readlines count overflow.
How do I count lines in a large file in C#?
Use a StreamReader count lines loop with an explicit buffer and FileOptions.SequentialScan, or use byte scanning when text decoding is unnecessary.
How do I count lines asynchronously in C#?
Use StreamReader.ReadLineAsync(CancellationToken) on .NET 6+ with a FileStream opened using FileOptions.Asynchronous.
What is the fastest way to count lines in C#?
For raw newline counts, Span<T> count lines .NET byte scanning is usually fastest because it avoids string allocation. This is the performance path for c# count lines workloads where decoding is unnecessary.
How do I count non-empty lines in C#?
Use:
long count = File.ReadLines(path)
.LongCount(line => !string.IsNullOrWhiteSpace(line));
How do I count lines in a string in C#?
Use StringReader for text-line semantics, or count \n characters directly if raw newline behavior is acceptable.
Sources Checked
- Microsoft
File.ReadAllLinesdocumentation: https://learn.microsoft.com/dotnet/api/system.io.file.readalllines - Microsoft
File.ReadLinesdocumentation: https://learn.microsoft.com/dotnet/api/system.io.file.readlines - Microsoft
Enumerable.Countdocumentation forInt32return and overflow: https://learn.microsoft.com/dotnet/api/system.linq.enumerable.count - Microsoft
Enumerable.LongCountdocumentation forInt64return: https://learn.microsoft.com/dotnet/api/system.linq.enumerable.longcount - Microsoft
StreamReader.ReadLineand line-ending behavior: https://learn.microsoft.com/dotnet/api/system.io.streamreader.readline - Microsoft
StreamReader.ReadLineAsync(CancellationToken)documentation: https://learn.microsoft.com/dotnet/api/system.io.streamreader.readlineasync - Microsoft
FileOptions.SequentialScandocumentation: https://learn.microsoft.com/dotnet/api/system.io.fileoptions - Microsoft .NET 8 performance notes for
MemoryExtensions.Count: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/
Related Guides and Tools
- Java line counting (similar StreamReader patterns) for JVM services and large files.
- Python line counting for scripts, CSV exports, and benchmarks.
- Kotlin line counting for
useLines, coroutines, Android file URIs, and theSequencelifetime trap. - Swift line counting for
FileHandle,URL.lines, SwiftUI progress, and Apple memory warnings. - counting rows in SQL for database-side counts and estimates.
- Node.js line counting for streams and browser uploads.
- Line Counter tool for no-code file counting.
Need a Quick Line Count Without a .NET Project?
Paste your file into the Line Counter. It works on logs, CSVs, configs, and plain text files. No OverflowException, no OOM, just the number.
Frequently Asked Questions
How do I count lines in a file in C#?
Use the streaming method with LongCount for a concise count, or a StreamReader loop with a long counter for production code.
What is the difference between ReadAllLines and ReadLines?
The read-all method returns a string array. The streaming method returns IEnumerable<string> and reads lazily as you enumerate.
Why does File.ReadLines().Count() throw OverflowException?
LINQ Count returns Int32. If the sequence has more than 2,147,483,647 items, it throws OverflowException. Use LongCount or a manual long counter.
How do I count lines in a large file in C#?
Use StreamReader with an explicit buffer and FileOptions.SequentialScan, or use a byte-scanning method when you only need newline counts.
How do I count lines asynchronously in C#?
Use StreamReader.ReadLineAsync(CancellationToken) on .NET 6+ with a FileStream opened with FileOptions.Asynchronous.
What is the fastest way to count lines in C#?
A byte scan over a Span<byte> or pooled byte buffer is usually fastest because it avoids creating one string per line.
How do I count non-empty lines in C#?
Use File.ReadLines(path).LongCount(line => !string.IsNullOrWhiteSpace(line)).
How do I count lines in a string in C#?
Use StringReader for text-line semantics or count '\n' characters directly when raw newline behavior is enough.
Related Guides
16 min read
How to Count Lines in a File Using Java (6 Methods, Benchmarked)
Count lines in a file using Java — BufferedReader, Files.lines, LineNumberReader, BufferedInputStream, and more. Includes benchmark results for 5GB files and Java 8–17 examples.
20 min read
How to Count Lines in Python: 7 Methods, Benchmarked and Battle-Tested
Count lines in Python strings, text files, large files, and directories. Includes real performance benchmarks, empty file handling, splitlines vs split, and production-ready functions.
15 min read
How to Count Rows in SQL: The Complete Guide (MySQL, PostgreSQL, SQLite, SQL Server)
Master SQL row counting: COUNT(*) vs COUNT(1) vs COUNT(col), fast row estimates without full scans, and cross-database syntax for MySQL, PostgreSQL, SQLite, and SQL Server.
14 min read
How to Count Lines in a File Using Swift (And the autoreleasepool Trap That Crashes Your App)
Count lines in a file using Swift — String(contentsOfFile), FileHandle, and async/await. Covers the autoreleasepool memory trap, iOS memory warnings, and SwiftUI progress patterns with benchmarks.