文件 IO 与序列化

翻译整理自 File IO 与 Serialization。

1. File 静态类

using System.IO;

string text = File.ReadAllText("a.txt");
string[] lines = File.ReadAllLines("a.txt");
byte[] bytes = File.ReadAllBytes("b.bin");

File.WriteAllText("out.txt", "hello");
File.WriteAllLines("out.txt", new[] { "a", "b" });
File.WriteAllBytes("out.bin", new byte[] { 1, 2 });

File.AppendAllText("log.txt", "new\n");

File.Exists("a.txt");
File.Delete("a.txt");
File.Copy("a", "b");
File.Move("a", "b");

2. 文件流 FileStream

using var fs = new FileStream("data.bin", FileMode.Create, FileAccess.Write);
fs.WriteByte(1);
fs.Write(new byte[] { 2, 3, 4 });
using var fs = new FileStream("data.bin", FileMode.Open);
int b;
while ((b = fs.ReadByte()) != -1) Console.Write(b);

3. StreamReader / Writer

using var sr = new StreamReader("a.txt");
string line;
while ((line = sr.ReadLine()) != null)
    Console.WriteLine(line);

using var sw = new StreamWriter("out.txt");
sw.WriteLine("hello");
sw.Write("world");

异步读写

using var sr = new StreamReader("a.txt");
string all = await sr.ReadToEndAsync();

await using var sw = new StreamWriter("out.txt");
await sw.WriteLineAsync("hello");

4. Directory

Directory.CreateDirectory("a/b/c");

string[] files = Directory.GetFiles("a");
string[] dirs = Directory.GetDirectories("a");

Directory.Delete("a", recursive: true);
Directory.Exists("a");
Directory.Move("a", "b");

var all = Directory.EnumerateFiles("a", "*.cs", SearchOption.AllDirectories);

5. Path

string dir = Path.GetDirectoryName("a/b.txt");        // a
string fn  = Path.GetFileName("a/b.txt");            // b.txt
string ext = Path.GetExtension("a.txt");             // .txt
string n   = Path.GetFileNameWithoutExtension("a.txt"); // a
string p  = Path.Combine("a", "b", "c.txt");          // a/b/c.txt

string tmp = Path.GetTempFileName();
string tmpDir = Path.GetTempPath();

6. JSON 序列化

System.Text.Json(.NET Core 3.0+ 起内置):

using System.Text.Json;

var u = new User { Name = "Alice", Age = 30 };

// 序列化
string json = JsonSerializer.Serialize(u, new JsonSerializerOptions
{
    WriteIndented = true,
    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
});

// 反序列化
User obj = JsonSerializer.Deserialize<User>(json);

// 异步
await using var fs = File.Create("u.json");
await JsonSerializer.SerializeAsync(fs, u);

选项:

var opt = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
};

中文/特殊字符需要 UnsafeRelaxedJsonEscaping 否则会被 \uXXXX 转义。

7. JSON 字符串/Stream API

using var doc = JsonDocument.Parse(json);
JsonElement root = doc.RootElement;
foreach (var p in root.EnumerateObject())
    Console.WriteLine($"{p.Name}: {p.Value}");

var writer = new Utf8JsonWriter(File.OpenWrite("o.json"));
writer.WriteStartObject();
writer.WriteString("name", "x");
writer.WriteEndObject();

8. Xml

using System.Xml.Serialization;

var ser = new XmlSerializer(typeof(User));
using var fs = File.OpenRead("u.xml");
var u = (User)ser.Deserialize(fs);

using var w = File.Create("o.xml");
ser.Serialize(w, u);

9. CSV / 自定义

简单 CSV:

var lines = File.ReadAllLines("data.csv");
foreach (var line in lines)
{
    var cols = line.Split(',');
    Console.WriteLine(string.Join(" | ", cols));
}

10. 内存映射文件

using var mmf = MemoryMappedFile.CreateFromFile("big.bin");
using var acc = mmf.CreateViewAccessor();
for (long i = 0; i < 1L << 30; i++)
    acc.ReadByte(i);

11. Zip

using System.IO.Compression;

ZipFile.CreateFromDirectory("a", "a.zip");
ZipFile.ExtractToDirectory("a.zip", "out");

using var za = ZipFile.OpenRead("a.zip");
foreach (var e in za.Entries) { ... }

小结

掌握 File / Stream / Json 后,IO 编程已不再是难点。Async + using 让资源管理异常安全。