C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
70: Working with CSV Data in C#
I remember a junior dev on my team a few years back who spent an entire Tuesday debugging a "data misalignment" issue. He was importing a list of client addresses from a CSV file. Everything worked perfectly for the first hundred records, but then he hit a record for a company located in "Austin, Texas". Because he had used a simple .Split(',') to parse the lines, that one comma inside the city/state field shifted every subsequent piece of data one column to the right. The zip code ended up in the "Notes" field, and the "Notes" field was null. He spent hours convinced the source file was corrupted, only to realize his parser wasn't actually "CSV-aware."
CSV files look simple because they're just text, but the "Comma Separated Values" specification is deceptively tricky. You have to deal with quoted fields, escaped characters, and varying line endings. While you can try to write a complex Regular Expression to handle these edges, I strongly advise against it. In a professional environment, we use libraries that have already solved these edge cases so we can focus on the actual business logic.
The Trap of the Simple Split
If you're dealing with a tiny, controlled internal file where you know for a fact that no comma will ever appear inside a data field, File.ReadAllLines() combined with string.Split() is tempting. It's fast to write. But as soon as a user enters "Company, Inc." into a text box that eventually ends up in your CSV, your application will crash or, worse, silently corrupt your database.
// The "Danger Zone" approach - avoid this for production data
var lines = File.ReadAllLines("data.csv");
foreach (var line in lines)
{
var columns = line.Split(','); // This is where the "Austin, Texas" bug happens
var name = columns[0];
var city = columns[1];
}
Real-world CSVs often wrap fields in double quotes ("Field, with comma") to protect the delimiter. A manual split completely ignores this logic. This is why I always reach for CsvHelper. It's the industry standard for C# and handles the RFC 4180 specification—the "gold standard" for CSVs—without you having to think about it.
Leveraging CsvHelper for Robustness
Once you've added the CsvHelper NuGet package to your project, the workflow shifts from manipulating strings to manipulating objects. Instead of worrying about which index in an array holds the "Price" value, you map the CSV columns directly to a C# class. This makes your code type-safe and significantly easier to maintain.
Here is how I usually set up a basic read operation. Notice how we wrap the StreamReader and CsvReader in using blocks to ensure the file handle is released immediately after we're done.
using System.Globalization;
using CsvHelper;
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
// ... inside a method ...
using (var reader = new StreamReader("products.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
// GetRecords lazily yields the objects as they are read
var records = csv.GetRecords<Product>().ToList();
foreach (var product in records)
{
Console.WriteLine($"{product.Name} costs {product.Price:C}");
}
}
Mapping Data to Strong Types
In a perfect world, your CSV headers would exactly match your C# property names. In the real world, you'll often get files with headers like "Prod_ID" or "Unit Price (USD)". You don't have to rename your C# properties to match these ugly headers; you can use a ClassMap.
A map tells CsvHelper: "When you see the column named 'Prod_ID', put that value into the 'Id' property." This separates your internal data model from the external file format, which is a crucial boundary to maintain in any professional app.
public sealed class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Map(m => m.Id).Name("Prod_ID");
Map(m => m.Name).Name("Product Name");
Map(m => m.Price).Name("Unit Price (USD)");
}
}
// To use the map:
using (var reader = new StreamReader("products.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Context.RegisterClassMap<ProductMap>();
var records = csv.GetRecords<Product>().ToList();
}📋 Practical Task
Exercise: Bookstore Inventory Import Tool
You have been tasked with building a small utility for a local bookstore. They provide you with a CSV file named inventory.csv. The catch is that their file is formatted inconsistently: some book titles contain commas, and the headers use non-standard naming.
The CSV Format:
Book_Title,Author_Name,ISBN_Code,Price_Per_Unit
"The Great Gatsby, Deluxe Edition",F. Scott Fitzgerald,9780743273565,15.99
"1984",George Orwell,9780451524935,12.50
"The Hobbit",J.R.R. Tolkien,9780547928227,20.00
Your Requirements:
- Create a
Bookclass with properties forTitle,Author,Isbn, andPrice. - Implement a
BookMapclass to map the CSV headers (e.g.,Book_Title) to your class properties. - Write a program that reads the
inventory.csvfile usingCsvHelper. - After loading the data, calculate and print the total value of the entire inventory (the sum of all prices) to the console.
- Ensure your code handles the quoted titles correctly (which
CsvHelperdoes by default, but you must implement it correctly).
There are no comments for now.