|
|
|
|
|
C# .NET 使用 CsvHelper 讀寫(xiě)CSV文件,是一個(gè)簡(jiǎn)單易用的值得推薦的方法,在前文中,我詳細(xì)介紹了 CsvHelper 的特征、用法等知識(shí),在本文中,將通過(guò)11個(gè)示例詳細(xì)介紹如何使用 CsvHelper 讀寫(xiě)CSV文件。
文章內(nèi)容目錄
先決條件
以下是使用 CsvHelper 所需的一些先決條件,這些是使用 CsvHelper 時(shí)隱含的 .NET 基礎(chǔ)知識(shí)。
每當(dāng)你有一個(gè)對(duì)象實(shí)現(xiàn)IDisposable
時(shí),你需要在用完資源時(shí)釋放它。大多數(shù)使用非托管資源的類(lèi)都將實(shí)現(xiàn)IDisposable
,這意味著System.IO
命名空間中的許多類(lèi)都需要被銷(xiāo)毀。
處理完對(duì)象的最佳做法是將代碼包裝在一個(gè)using
塊中,當(dāng)using
塊退出時(shí),資源將盡快自動(dòng)處理。
using (var stream = new MemoryStream())
{
// Use the stream.
}
// The stream will be disposed of as soon as possible.
如果你需要保留它一段時(shí)間并在以后處理它,using
為你做一些錯(cuò)誤處理,所以使用它而不是Dispose
直接調(diào)用仍然是個(gè)好主意。
var stream = new MemoryStream();
// Later in a different part of your code.
using (stream) { }
要打開(kāi)文件進(jìn)行讀取或?qū)懭耄覀兛梢允褂?code>System.IO.File。
using (var stream = File.OpenRead("path\\to\\file.csv"))
{
}
using (var stream = File.OpenWrite("path\\to\\file.csv"))
{
}
這些都返回一個(gè)FileStream
用于處理我們的文件。由于我們的數(shù)據(jù)是文本,因此我們需要使用 StreamReader
和 StreamWriter
來(lái)讀寫(xiě)文本。
using (var stream = File.OpenRead("path\\to\\file.csv"))
using (var reader = new StreamReader(stream))
{
}
using (var stream = File.OpenWrite("path\\to\\file.csv"))
using (var writer = new StreamWriter(stream))
{
}
StreamReader
和StreamWriter
有執(zhí)行此操作的快捷方式。
using (var reader = new StreamReader("path\\to\\file.csv"))
{
}
using (var writer = new StreamWriter("path\\to\\file.csv"))
{
}
CsvHelper 對(duì)你的編碼一無(wú)所知,因此如果你有特定的編碼,則需要在流中指定它。
using (var reader = new StreamReader("path\\to\\file.csv", Encoding.UTF8))
{
}
using (var writer = new StreamWriter("path\\to\\file.csv", Encoding.UTF8))
{
}
CsvReader
和 CsvWriter
在他們的構(gòu)造函數(shù)中使用 TextReader
和 TextWriter
。TextReader
和 TextWriter
是閱讀和寫(xiě)作文本的abstract
類(lèi)。StreamReader
繼承 TextReader
, StreamWriter
繼承TextWriter
,所以我們可以使用CsvReader
和CsvWriter
。
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader))
{
}
using (var writer = new StreamWriter("path\\to\\file.csv"))
using (var csv = new CsvWriter(writer))
{
}
從流中讀取時(shí),如果需要回到流的開(kāi)頭,可以使用 Stream.Position
屬性。
using (var stream = new File.OpenRead("path\\to\\file"))
using (var reader = new StreamReader(stream))
{
// Read file content.
var content = reader.ReadToEnd();
// Go back to beginning of the stream.
stream.Position = 0;
// Read file content again.
content = reader.ReadToEnd();
}
寫(xiě)入文件時(shí),需要刷新寫(xiě)入器才能將數(shù)據(jù)寫(xiě)入流。StreamWriter
包含一個(gè)內(nèi)部緩沖區(qū),數(shù)據(jù)僅在緩沖區(qū)已滿(mǎn)或被調(diào)用Flush
時(shí)寫(xiě)入流。當(dāng)using
塊退出時(shí)自動(dòng)調(diào)用Flush
。
using (var stream = new File.OpenWrite("path\\to\\file"))
using (var writer = new StreamWriter(stream))
{
writer.WriteLine("Foo");
writer.Flush(); // Data is written from the writer buffer to the stream.
} // Flush is also called here.
讀CSV文件[示例]
將 CSV 行轉(zhuǎn)換為類(lèi)對(duì)象。
數(shù)據(jù)
Id,Name
1,one
例子
void Main()
{
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = csv.GetRecords<Foo>();
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
將 CSV 行轉(zhuǎn)換為動(dòng)態(tài)對(duì)象。由于無(wú)法確定屬性應(yīng)該是什么類(lèi)型,因此動(dòng)態(tài)對(duì)象上的所有屬性都是字符串。
數(shù)據(jù)
Id,Name
1,one
例子
void Main()
{
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = csv.GetRecords<dynamic>();
}
}
將 CSV 行轉(zhuǎn)換為匿名類(lèi)型對(duì)象。你只需要提供匿名類(lèi)型定義。
數(shù)據(jù)
Id,Name
1,one
例子
void Main()
{
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var anonymousTypeDefinition = new
{
Id = default(int),
Name = string.Empty
};
var records = csv.GetRecords(anonymousTypeDefinition);
}
}
將 CSV 行轉(zhuǎn)換為在可枚舉的每次迭代中重復(fù)使用的類(lèi)對(duì)象。每個(gè)枚舉都會(huì)混合給定的記錄,但僅限于映射的成員。如果你提供了一個(gè)映射但沒(méi)有映射其中一個(gè)成員,則該成員將不會(huì)與當(dāng)前行的數(shù)據(jù)混合。當(dāng)心在強(qiáng)制IEnumerable
評(píng)估的投影上調(diào)用的任何方法,例如ToList()
,你將獲得一個(gè)列表,其中所有記錄都是你提供的與 CSV 文件中的最后一條記錄混合的相同實(shí)例。
數(shù)據(jù)
Id,Name
1,one
例子
void Main()
{
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var record = new Foo();
var records = csv.EnumerateRecords(record);
foreach (var r in records)
{
// r is the same instance as record.
}
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
有時(shí)出于各種原因,不嘗試配置映射以匹配你的類(lèi)定義會(huì)更容易。通常只需要多幾行代碼就可以手動(dòng)讀取行。
數(shù)據(jù)
Id,Name
1,one
例子
void Main()
{
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = new List<Foo>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var record = new Foo
{
Id = csv.GetField<int>("Id"),
Name = csv.GetField("Name")
};
records.Add(record);
}
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
出于某種原因,存在包含多組 CSV 數(shù)據(jù)的 CSV 文件。你應(yīng)該能夠毫無(wú)問(wèn)題地讀取這樣的文件。你將需要檢測(cè)何時(shí)更改要檢索的類(lèi)類(lèi)型。
數(shù)據(jù)
FooId,Name
1,foo
BarId,Name
07a0fca2-1b1c-4e44-b1be-c2b05da5afc7,bar
例子
void Main()
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
IgnoreBlankLines = false,
};
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, config))
{
csv.Context.RegisterClassMap<FooMap>();
csv.Context.RegisterClassMap<BarMap>();
var fooRecords = new List<Foo>();
var barRecords = new List<Bar>();
var isHeader = true;
while (csv.Read())
{
if (isHeader)
{
csv.ReadHeader();
isHeader = false;
continue;
}
if (string.IsNullOrEmpty(csv.GetField(0)))
{
isHeader = true;
continue;
}
switch (csv.HeaderRecord[0])
{
case "FooId":
fooRecords.Add(csv.GetRecord<Foo>());
break;
case "BarId":
barRecords.Add(csv.GetRecord<Bar>());
break;
default:
throw new InvalidOperationException("Unknown record type.");
}
}
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Bar
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public sealed class FooMap : ClassMap<Foo>
{
public FooMap()
{
Map(m => m.Id).Name("FooId");
Map(m => m.Name);
}
}
public sealed class BarMap : ClassMap<Bar>
{
public BarMap()
{
Map(m => m.Id).Name("BarId");
Map(m => m.Name);
}
}
如果你有 CSV 數(shù)據(jù),其中每一行可能是不同的記錄類(lèi)型,你應(yīng)該能夠根據(jù)行類(lèi)型或類(lèi)似的東西讀取。
數(shù)據(jù)
A,1,foo
B,07a0fca2-1b1c-4e44-b1be-c2b05da5afc7,bar
例子
void Main()
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
};
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, config))
{
csv.Context.RegisterClassMap<FooMap>();
csv.Context.RegisterClassMap<BarMap>();
var fooRecords = new List<Foo>();
var barRecords = new List<Bar>();
while (csv.Read())
{
switch (csv.GetField(0))
{
case "A":
fooRecords.Add(csv.GetRecord<Foo>());
break;
case "B":
barRecords.Add(csv.GetRecord<Bar>());
break;
default:
throw new InvalidOperationException("Unknown record type.");
}
}
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Bar
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public sealed class FooMap : ClassMap<Foo>
{
public FooMap()
{
Map(m => m.Id).Index(1);
Map(m => m.Name).Index(2);
}
}
public sealed class BarMap : ClassMap<Bar>
{
public BarMap()
{
Map(m => m.Id).Index(1);
Map(m => m.Name).Index(2);
}
}
寫(xiě)CSV文件[示例]
由于篇幅有限,CsvHelper 寫(xiě)CSV文件[示例]請(qǐng)參閱下一篇文章:
相關(guān)文章