CSV/TSV 等分隔符文字檔是常見資料交換格式。C# LINQ 提供 `File.ReadAllLines` 搭配 `Select`、`Where` 即可以聲明式方式完成「讀取 → 分割 → 映射 → 篩選」的完整管線,程式碼簡潔且具備延遲執行特性。本文示範基本讀取、條件篩選、多條件查詢三種常見場景。
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string JobTitle { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
string filePath = @"C:\Data\customers.csv";
// 一次性讀取所有行 (小檔案適用),大檔案請用 File.ReadLines 流式處理
var query = from line in File.ReadAllLines(filePath)
let fields = line.Split(',')
select new Customer
{
FirstName = fields[0],
LastName = fields[1],
JobTitle = fields[2],
City = fields[3],
Country = fields[4]
};
foreach (var c in query)
{
Console.WriteLine($"{c.FirstName}, {c.LastName}, {c.JobTitle}, {c.City}, {c.Country}");
}
var ukCustomers = from c in
(from line in File.ReadAllLines(filePath)
let fields = line.Split(',')
select new Customer
{
FirstName = fields[0],
LastName = fields[1],
JobTitle = fields[2],
City = fields[3],
Country = fields[4]
})
where c.Country == "UK"
select c;
var salesStaff = from c in
(from line in File.ReadAllLines(filePath)
let fields = line.Split(',')
select new Customer
{
FirstName = fields[0],
LastName = fields[1],
JobTitle = fields[2],
City = fields[3],
Country = fields[4]
})
where c.JobTitle.Contains("Sales")
select c;
上述寫法每個查詢都重複 `Split` 與 `new Customer`。實務上建議先物質化為 `List
// 方法 A: 先載入清單 (小檔案)
var allCustomers = File.ReadAllLines(filePath)
.Select(line => line.Split(','))
.Select(f => new Customer { FirstName=f[0], LastName=f[1], JobTitle=f[2], City=f[3], Country=f[4] })
.ToList(); // 物質化
var uk = allCustomers.Where(c => c.Country == "UK");
var sales = allCustomers.Where(c => c.JobTitle.Contains("Sales"));
// 方法 B: 大檔案使用 File.ReadLines (延遲串流) + 封裝迭代器
IEnumerable ReadCustomers(string path)
{
foreach (var line in File.ReadLines(path))
{
var f = line.Split(',');
yield return new Customer { FirstName=f[0], LastName=f[1], JobTitle=f[2], City=f[3], Country=f[4] };
}
}
// 後續查詢皆為串流處理,記憶體常數
var ukStream = ReadCustomers(filePath).Where(c => c.Country == "UK");
| 問題 | 解決方案 |
|---|---|
| 欄位含逗號/換行 (CSV 標準) | 勿用 `Split(',')`,改用 `TextFieldParser` (Microsoft.VisualBasic.FileIO) 或 `CsvHelper` 套件 |
| 編碼非 UTF-8 (Big5/GB2312) | `File.ReadAllLines(path, Encoding.GetEncoding("Big5"))` |
| 標題列需跳過 | `.Skip(1)` 或 `File.ReadLines().Skip(1)` |
| 欄位數不固定 | 檢查 `fields.Length`、缺欄位補 `null`/空字串 |
| 大檔案記憶體爆炸 | 改用 `File.ReadLines` + `yield return` 串流處理 |
| 方法 | 記憶體 | 速度 | 適用場景 |
|---|---|---|---|
| File.ReadAllLines + LINQ | O(N) 全載入 | 快 | < 50MB、一次性處理 |
| File.ReadLines + yield | O(1) 串流 | 稍慢 | 大檔案、管線處理 |
| CsvHelper | 可串流 | 快 (優化) | 標準 CSV、生產環境 |
LINQ + `File.ReadAllLines`/`ReadLines` 以極少程式碼完成文字檔讀取、映射、篩選。關鍵在於「先定義實體、再建立查詢管線、最後依需求物質化或串流」。生產環境涉及標準 CSV 複雜格式時,強烈建議引入 `CsvHelper` 避免手工解析 Bug。