In modern software architecture, data interoperability is everything. While spreadsheet applications like Microsoft Excel are the preferred interface for business users to view and analyze data, machines and backend systems vastly prefer Comma-Separated Values (CSV). CSV is lightweight, plaintext, universally compatible, and simple to parse.
As a .NET developer, you will frequently be tasked with bridging this divide. Whether you need to build an automated background job to ingest supplier spreadsheets or build an export tool for data pipelines, understanding how to handle xls to csv c# conversions—and vice versa—is a critical skill.
However, if you search the web for solutions, you will find a landscape cluttered with outdated tutorials, commercial-only library promotions, and dangerous advice involving COM Interop. This guide cuts through the noise. We will cover the most robust, high-performance, and completely free cross-platform methods to convert XLS to CSV and CSV to XLS in C# without needing Microsoft Excel installed.
Understanding the Formats: XLS vs. XLSX vs. CSV
Before writing a single line of code, we must address the most common mistake developers make: XLS is not the same as XLSX, and treating them as identical will break your application in production.
- XLS (Excel 97-2003 Spreadsheet): This is a legacy, proprietary binary format based on Microsoft’s Compound File Binary Format (CFBF). It uses the BIFF8 specification. Reading and writing legacy
.xlsfiles requires specialized binary parsers because the internal structure is extremely complex. - XLSX (Excel Open XML Spreadsheet): Introduced in Excel 2007,
.xlsxis an open standard format based on XML. Under the hood, an.xlsxfile is actually a zipped package containing multiple XML files that define sheet data, styles, relationships, and metadata. - CSV (Comma-Separated Values): A pure plaintext format where each row of data is written on a new line, and cells are separated by a delimiter (typically a comma). It contains zero formatting, zero styling, no formulas, and only supports a single sheet.
Why Library Selection Matters
Because of the vast architectural differences between legacy XLS and modern XLSX, many popular open-source .NET libraries only support one format.
For example, highly-regarded libraries like ClosedXML and EPPlus are strictly designed around the OpenXML standard. If you attempt to feed a legacy .xls binary file into ClosedXML, it will immediately throw a FileFormatException. Conversely, read-only libraries like ExcelDataReader are great because they auto-detect and read both formats, but they cannot write data back to spreadsheets.
This table outlines the capabilities of the primary .NET libraries to help you choose the right tool:
| Library | Read Legacy XLS? | Write Legacy XLS? | Read Modern XLSX? | Write Modern XLSX? | License | Main Use Case |
|---|---|---|---|---|---|---|
| ExcelDataReader | Yes | No | Yes | No | MIT (Free) | Fast, low-memory extraction of raw spreadsheet data. |
| NPOI | Yes | Yes | Yes | Yes | Apache 2.0 (Free) | Native reading and writing of legacy .xls files. |
| ClosedXML | No | No | Yes | Yes | MIT (Free) | Intuitive, robust creation and editing of modern .xlsx sheets. |
| EPPlus (v5+) | No | No | Yes | Yes | Non-Commercial / Paid | Professional Excel styling, formula evaluation, and charting. |
| Aspose.Cells | Yes | Yes | Yes | Yes | Commercial (Paid) | Enterprise workflows with highly complex layouts and conversions. |
| COM Interop | Yes | Yes | Yes | Yes | Proprietary | Never use in production/server environments. |
Why You Must Avoid Microsoft.Office.Interop.Excel
Older .NET applications often relied on COM Interop (Microsoft.Office.Interop.Excel) to convert spreadsheets. Microsoft explicitly advises against using this in server-side, multi-threaded, or web environments. COM Interop requires MS Excel to be physically installed on the host machine. It is extremely slow, single-threaded, highly unstable, and prone to unmanaged memory leaks that can bring down your IIS server. All solutions presented in this guide are 100% No-Interop and run cross-platform on Windows, Linux, and macOS.
How to Convert XLS to CSV in C#
When your primary objective is to convert xls to csv c#, you want an approach that is fast, robust against formatting edge cases, and completely open-source.
Below, we explore the two best open-source methods—one using a combination of ExcelDataReader and CsvHelper (best for streaming speed), and one using NPOI (best for all-in-one spreadsheet processing).
Method 1: The Gold Standard — ExcelDataReader & CsvHelper
This approach uses ExcelDataReader to read the complex binary XLS stream and CsvHelper to write out the fields cleanly. We use CsvHelper because writing a CSV is more than just joining strings with a comma (string.Join(",", values)). If your spreadsheet contains cell data with actual commas, line breaks, or quotation marks, a basic string join will corrupt the CSV structure. CsvHelper automatically handles all escaping, formatting, and quoting rules.
Step 1: Install NuGet Packages
Run the following commands in your Package Manager Console:
PM> Install-Package ExcelDataReader
PM> Install-Package ExcelDataReader.DataSet
PM> Install-Package CsvHelper
Step 2: Implement the C# XLS to CSV Code
using System;
using System.IO;
using System.Text;
using System.Globalization;
using ExcelDataReader;
using CsvHelper;
namespace SpreadsheetConverter
{
public class LegacyConverter
{
/// <summary>
/// Converts a legacy .xls or modern .xlsx file to a CSV file cleanly.
/// </summary>
/// <param name="sourceFilePath">Path to the input XLS/XLSX file.</param>
/// <param name="destinationCsvPath">Path where the output CSV will be saved.</param>
public static void ConvertXlsToCsv(string sourceFilePath, string destinationCsvPath)
{
// CRITICAL: Register CodePagesEncodingProvider.
// Legacy .xls files rely on historical encoding pages. Without registering this,
// .NET Core and .NET 5+ will throw a "NotSupportedException: No data is available for encoding."
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using (var excelStream = File.Open(sourceFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
// CreateReader automatically auto-detects if the input stream is legacy .xls or .xlsx
using (var reader = ExcelReaderFactory.CreateReader(excelStream))
{
using (var csvStreamWriter = new StreamWriter(destinationCsvPath, false, Encoding.UTF8))
using (var csvWriter = new CsvWriter(csvStreamWriter, CultureInfo.InvariantCulture))
{
// ExcelDataReader reads sheet by sheet. We will process each sheet.
do
{
while (reader.Read()) // Reads row-by-row
{
int fieldCount = reader.FieldCount;
for (int i = 0; i < fieldCount; i++)
{
// Retrieve the cell value and parse it safely
object cellValue = reader.GetValue(i);
string stringValue = cellValue?.ToString() ?? string.Empty;
// CsvHelper takes care of internal quotes, commas, and formatting
csvWriter.WriteField(stringValue);
}
// Move the CSV writer to a new row
csvWriter.NextRecord();
}
} while (reader.NextResult()); // Moves to the next worksheet if it exists
}
}
}
}
}
}
Method 2: The All-in-One Framework — NPOI
If you prefer to avoid chaining multiple packages together, NPOI can handle the heavy lifting of reading legacy .xls files directly. NPOI is a robust, mature library that excels at parsing binary BIFF8 (.xls) file structures natively.
Step 1: Install NuGet Package
PM> Install-Package NPOI
Step 2: Implement the NPOI Code
using System;
using System.IO;
using System.Text;
using NPOI.HSSF.UserModel; // For legacy HSSF XLS
using NPOI.SS.UserModel;
namespace SpreadsheetConverter
{
public class NpoiConverter
{
public static void ConvertXlsToCsv(string xlsPath, string csvPath)
{
using (var fileStream = new FileStream(xlsPath, FileMode.Open, FileAccess.Read))
{
// Load the workbook using HSSFWorkbook (designed specifically for binary .xls)
HSSFWorkbook workbook = new HSSFWorkbook(fileStream);
// For simplicity, we convert the first worksheet (index 0)
ISheet sheet = workbook.GetSheetAt(0);
using (var streamWriter = new StreamWriter(csvPath, false, Encoding.UTF8))
{
for (int r = 0; r <= sheet.LastRowNum; r++)
{
IRow row = sheet.GetRow(r);
if (row == null)
{
// Write an empty row in CSV if the row is null in the sheet
streamWriter.WriteLine();
continue;
}
var rowValues = new string[row.LastCellNum];
for (int c = 0; c < row.LastCellNum; c++)
{
ICell cell = row.GetCell(c);
rowValues[c] = GetFormattedCellValue(cell);
}
// Manually compile the CSV line, applying strict RFC 4180 escaping rules
string csvLine = BuildSafeCsvLine(rowValues);
streamWriter.WriteLine(csvLine);
}
}
}
}
private static string GetFormattedCellValue(ICell cell)
{
if (cell == null) return string.Empty;
switch (cell.CellType)
{
case CellType.Numeric:
if (DateUtil.IsCellDateFormatted(cell))
{
return cell.DateCellValue.ToString("yyyy-MM-dd HH:mm:ss");
}
return cell.NumericCellValue.ToString(System.Globalization.CultureInfo.InvariantCulture);
case CellType.Boolean:
return cell.BooleanCellValue.ToString();
case CellType.Formula:
try
{
// Evaluate formulas if possible
return cell.StringCellValue ?? cell.NumericCellValue.ToString(System.Globalization.CultureInfo.InvariantCulture);
}
catch
{
return cell.ToString();
}
default:
return cell.StringCellValue ?? cell.ToString();
}
}
private static string BuildSafeCsvLine(string[] fields)
{
for (int i = 0; i < fields.Length; i++)
{
string val = fields[i] ?? string.Empty;
// If fields contain comma, double quotes, or newlines, they must be escaped
if (val.Contains(",") || val.Contains("\"") || val.Contains("\n") || val.Contains("\r"))
{
// Escape inner double quotes by doubling them, then wrap the entire field
val = "\"" + val.Replace("\"", "\"\"") + "\"";
}
fields[i] = val;
}
return string.Join(",", fields);
}
}
}
How to Convert CSV to XLS/XLSX in C#
When writing a converter for csv to xls c#, you must make a stylistic choice. While the query is "CSV to XLS", generating legacy binary .xls files is generally discouraged unless your target infrastructure is heavily outdated. Modern applications overwhelmingly use OpenXML .xlsx for spreadsheet generation.
In this section, we show you both: how to write a real, legacy binary .xls file using NPOI, and how to write a modern, enterprise-ready .xlsx spreadsheet using ClosedXML.
Method 1: True Legacy CSV to XLS Conversion (Using NPOI)
If you have a legacy system that strictly accepts .xls binary files, NPOI is your ultimate weapon. We combine NPOI with .NET's built-in TextFieldParser (found in the Microsoft.VisualBasic.FileIO namespace, but perfectly compatible with modern cross-platform .NET projects) to read the CSV and build a structured binary sheet.
Code Implementation:
using System;
using System.IO;
using NPOI.HSSF.UserModel; // For XLS generation
using NPOI.SS.UserModel;
using Microsoft.VisualBasic.FileIO;
namespace SpreadsheetConverter
{
public class CsvToLegacyXlsConverter
{
public static void ConvertCsvToXls(string csvPath, string destinationXlsPath)
{
// Initialize an HSSFWorkbook to write out legacy binary formats
HSSFWorkbook workbook = new HSSFWorkbook();
ISheet sheet = workbook.CreateSheet("Imported Data");
using (TextFieldParser csvParser = new TextFieldParser(csvPath))
{
csvParser.TextFieldType = FieldType.Delimited;
csvParser.SetDelimiters(",");
csvParser.HasFieldsEnclosedInQuotes = true;
int rowCount = 0;
while (!csvParser.EndOfData)
{
string[] fields = csvParser.ReadFields();
IRow row = sheet.CreateRow(rowCount++);
for (int i = 0; i < fields.Length; i++)
{
ICell cell = row.CreateCell(i);
string rawValue = fields[i];
// Smart type parsing to avoid writing everything as plain text
if (double.TryParse(rawValue, out double dblValue))
{
cell.SetCellValue(dblValue);
}
else if (bool.TryParse(rawValue, out bool boolValue))
{
cell.SetCellValue(boolValue);
}
else
{
cell.SetCellValue(rawValue);
}
}
}
}
// Write the actual binary bytes to disk
using (FileStream fs = new FileStream(destinationXlsPath, FileMode.Create, FileAccess.Write))
{
workbook.Write(fs);
}
}
}
}
Method 2: Modern CSV to XLSX Conversion (Using ClosedXML)
For 99% of business scenarios, you will want to generate an OpenXML .xlsx file. ClosedXML is the perfect library for this task—it is highly intuitive, lightweight, and incredibly fast.
Step 1: Install NuGet Package
PM> Install-Package ClosedXML
Step 2: Implement the ClosedXML Code
using System;
using System.IO;
using ClosedXML.Excel;
using Microsoft.VisualBasic.FileIO;
namespace SpreadsheetConverter
{
public class CsvToModernXlsx
{
public static void ConvertCsvToXlsx(string csvPath, string xlsxPath)
{
using (var workbook = new XLWorkbook())
{
var worksheet = workbook.Worksheets.Add("Imported CSV");
using (TextFieldParser parser = new TextFieldParser(csvPath))
{
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
parser.HasFieldsEnclosedInQuotes = true;
int rowNumber = 1; // Note: ClosedXML relies on 1-based indexing for rows and columns
while (!parser.EndOfData)
{
string[] fields = parser.ReadFields();
for (int colIndex = 1; colIndex <= fields.Length; colIndex++)
{
string cellValue = fields[colIndex - 1];
// Preserve data integrity by detecting correct value types
if (double.TryParse(cellValue, out double numericVal))
{
worksheet.Cell(rowNumber, colIndex).SetValue(numericVal);
}
else if (DateTime.TryParse(cellValue, out DateTime dateVal))
{
worksheet.Cell(rowNumber, colIndex).SetValue(dateVal);
}
else
{
// By default, write it as a raw string
worksheet.Cell(rowNumber, colIndex).SetValue(cellValue);
}
}
rowNumber++;
}
}
// Format the Excel Sheet on the fly
worksheet.Columns().AdjustToContents(); // Auto-fit all column widths
workbook.SaveAs(xlsxPath);
}
}
}
}
Performance & Memory Optimization Secrets
When handling basic, sub-megabyte spreadsheets, performance is rarely an issue. However, in enterprise systems handling millions of rows, spreadsheet conversions can quickly trigger an OutOfMemoryException or degrade CPU performance. Implement these patterns to write bulletproof, enterprise-ready conversion code.
1. Leverage Streaming Over In-Memory Ingestion
Avoid methods that load the entire dataset into memory (such as converting the entire Excel file into a DataSet object). Libraries like ExcelDataReader are built with a low-memory, stream-oriented reader architecture. They pull records from the underlying XML or binary structure incrementally, keeping your application’s memory usage entirely flat regardless of whether the spreadsheet is 5MB or 500MB.
2. Guard Against the "Dropping Leading Zeros" Trap
If you have a CSV with postal codes (e.g., "02110") or account identifiers (e.g., "0094132"), simple parsers will automatically evaluate them as numeric. When written to Excel, these values will be coerced into standard floating-point numbers, instantly stripping away the leading zeros and converting "02110" into 2110—a disaster for downstream billing integrations.
The Fix: When performing c# convert csv to xls, inspect the headers or columns of your incoming CSV. If a column represents an identifier, force the data type explicitly as a string in the spreadsheet library rather than letting the parser auto-detect it:
// Force cell format as a String to prevent parsing numeric representations
var cell = worksheet.Cell(rowNumber, colIndex);
cell.SetValue(cellValue); // Write as raw string
cell.DataType = XLDataType.Text; // Explicitly enforce text type in ClosedXML
3. Handle Resource Disposal Meticulously
Spreadsheet parsing libraries are wrapping heavy system handles, compressed zip structures, and unmanaged byte arrays. Failing to dispose of these streams properly can cause rapid memory leaks. Always wrap your spreadsheet resources, stream writers, and file handles in standard C# using blocks to guarantee immediate disposal upon execution failure.
Frequently Asked Questions (FAQ)
Can I use ClosedXML or EPPlus to parse legacy .xls files?
No. Both ClosedXML and EPPlus are built exclusively to operate with the Office Open XML standard (.xlsx, .xlsm). Attempting to parse a legacy binary .xls file with them will result in a runtime FileFormatException or a corrupted file exception. To process legacy binary formats, utilize ExcelDataReader or NPOI.
How do I compile CSV output if my cell values contain actual commas?
Never write a manual string parsing algorithm that splits or joins on simple string arrays (e.g., line.Split(',')). If a cell contains a literal comma (such as "Seattle, WA"), your data layout will break. Always use established parsers like CsvHelper (for writing CSVs) or TextFieldParser (for reading CSVs). These conform to RFC 4180 standards and safely escape problematic commas using double-quote wrapping.
Why does ExcelDataReader throw a NotSupportedException regarding codepages?
Modern .NET Core and .NET Core implementations do not load international text codepages by default to maintain a small runtime package footprint. Because legacy .xls binary files rely on localized codepages to decode older text types, you must register the code page provider before attempting to read legacy sheets:
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
Can I run these .NET conversion scripts on Linux containers?
Yes, absolutely. Because all libraries referenced in this guide are built using pure C# (.NET Standard / modern .NET Core) and do not depend on native Windows binary DLLs or MS Office automation APIs, they will execute flawlessly on Linux containers, AWS ECS/Fargate, Azure Web Apps, and Docker containers.
How can I convert a multi-sheet spreadsheet into CSVs?
CSVs do not have a structural representation for multiple sheets. To convert a multi-sheet XLS file, you must loop through each worksheet and export each sheet as an independent CSV file. For example, using ExcelDataReader, you call reader.NextResult() to cycle to the next sheet, and save the resulting records to a unique file path (e.g., sheet1.csv, sheet2.csv).
Conclusion
Converting xls to csv c# and csv to xls c# doesn't have to be a headache. By matching the correct library to the structural constraints of your spreadsheet file, you can write clean, cross-platform code that handles massive enterprise datasets efficiently.
For read-only operations on legacy .xls files, leverage ExcelDataReader in tandem with CsvHelper. For structural manipulation of modern spreadsheets, ClosedXML offers the cleanest, most readable API on the market. Always avoid unmanaged Microsoft Office COM Interop dependencies to guarantee your system operates smoothly, stably, and without memory leaks in production environments.









