在 .NET 中使用文件和流(针对 .NET 8 /9 更新)

科技   2024-11-02 15:30   广东  

文件处理是大多数软件系统的重要组成部分,尤其是在处理自动化流程、日志记录或用户数据管理时。C# 提供了一组丰富的工具,用于通过命名空间处理文件和流,这些工具提供了从基本文件操作到高效处理大型数据集的高级技术的所有内容。System.IO

在这篇更新的文章中,我们将介绍 C# 中文件和流处理的基本概念,集成新功能和现代最佳实践。

1. C 语言中的文件 I/O 简介#

C# 中的文件 I/O 围绕两个关键概念:

  • 基于文件的 I/O:使用 、 和 等类直接与文件交互。FileFileInfoDirectory

  • 基于流的 I/O:在连续流(流)中处理数据,允许高效处理大型数据集,而无需将其完全加载到内存中。

为什么选择 Streams?

Streams 允许您的应用程序以增量方式处理大量数据。这在实时系统(如工业监控或大型自动化仓库系统)中特别有用,因为在这些系统中,性能和内存效率至关重要。Streams 提供了一种动态处理数据的方法,可保持较低的内存使用率和高吞吐量。

异步操作

对此的一个重要补充是异步文件操作,它已成为现代 C# 应用程序的标准。使用异步方法(如 、 和 ),您可以确保非阻塞 I/O 操作,这对于需要保持响应或受 I/O 限制的应用程序来说是一项关键功能。ReadAsyncWriteAsyncCopyToAsync

using (FileStream sourceStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true))  
{
byte[] buffer = new byte[1024];
int bytesRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length);
Console.WriteLine($"Bytes read: {bytesRead}");
}

在此示例中,确保主线程未被阻止,这对于处理大型文件的 UI 或 Web 应用程序特别有用。ReadAsync

2. 使用文件

命名空间提供了多个类,可简化 C# 中的文件处理。让我们回顾一下关键因素:System.IO

常见类

  • File:用于读取、写入和删除等常见文件操作的静态类。

  • FileInfo:提供一种面向对象的方式来访问文件元数据和操作,从而允许重复使用文件信息。

  • Directory:用于管理目录的静态类,包括创建、移动和枚举文件。

  • DirectoryInfo:与 ,但用于目录级操作。FileInfo

示例:读取和写入文件

// Writing text to a file  
string path = @"C:\\example\\log.txt";
await File.WriteAllTextAsync(path, "Log entry at: " + DateTime.Now.ToString());

// Reading the file asynchronously
string content = await File.ReadAllTextAsync(path);
Console.WriteLine("File Content: " + content);

在这里,我们使用异步方法 ( 和 ),在响应能力至关重要的情况下,这些方法是首选方法。这些方法允许您有效地处理中小型文件。WriteAllTextAsyncReadAllTextAsync

3. 使用 Streams

Streams 提供了对文件 I/O 的更精细的控制,尤其是在处理大型文件或二进制数据时。

FileStream 用于大文件

该类支持对文件的低级访问,通常用于需要以增量方式处理文件的方案。FileStream

// Writing to a file using FileStream  
using (FileStream fs = new FileStream(@"C:\example\output.dat", FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
{
byte[] data = System.Text.Encoding.UTF8.GetBytes("Streamed content.");
await fs.WriteAsync(data, 0, data.Length);
}

// Reading a large file in chunks
using (FileStream fs = new FileStream(@"C:\example\largefile.dat", FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[4096]; // 4KB buffer
int bytesRead;
while ((bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
// Process buffer here
Console.WriteLine($"Bytes read: {bytesRead}");
}
}

在处理大型文件时,例如数据处理管道中的日志文件,在这种情况下,将整个文件读入内存是不可行的,因此此模式非常有用。

4. 使用缓冲处理大文件

在处理大型文件时,使用缓冲读取和写入技术以避免内存过载非常重要。上面的示例使用缓冲区为 4096 字节的 a 是增量处理大型文件的常用方法。FileStream

对于高性能系统,请考虑将流与异步操作相结合。在处理大量数据或缓慢的 I/O 操作(例如,网络驱动器)时,异步文件访问和缓冲可以提高吞吐量,而不会使系统资源不堪重负。

// Asynchronously writing with a buffer  
byte[] largeBuffer = new byte[4096 * 100]; // 400KB buffer for demonstration
using (FileStream fs = new FileStream(@"C:\example\large\_output.dat", FileMode.Create, FileAccess.Write, FileShare.None, 4096, useAsync: true))
{
await fs.WriteAsync(largeBuffer, 0, largeBuffer.Length);
}

5. 内存映射文件(高级)

内存映射文件允许通过将非常大的文件直接映射到应用程序的地址空间来处理它们,从而提供随机访问,而无需将整个文件加载到内存中。

using (var mmf = MemoryMappedFile.CreateFromFile(@"C:\example\largefile.dat", FileMode.Open))  
{
using (var accessor = mmf.CreateViewAccessor(0, 1024))
{
byte[] data = new byte[1024];
accessor.ReadArray(0, data, 0, data.Length);
Console.WriteLine("Read first 1024 bytes from memory-mapped file.");
}
}

该技术在需要高速、随机访问大型文件的方案中特别有用,例如在数据库、大型图像处理或复杂模拟中。

6. 文件权限和锁定

文件 I/O 操作可能会因权限问题或文件锁定而失败,尤其是在多线程应用程序或网络环境中。妥善处理这些情况非常重要:

try  
{
using (var stream = new FileStream(@"C:\example\protected.txt", FileMode.Open))
{
// Process file
}
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("Access denied: " + ex.Message);
}
catch (IOException ex)
{
Console.WriteLine("File is locked or in use: " + ex.Message);
}

妥善处理异常(如或可以防止意外崩溃),并帮助在文件访问并发或受限的环境中管理 I/O。UnauthorizedAccessExceptionIOException

7. 跨平台文件 I/O 注意事项

随着 .NET Core 和 .NET 7 的兴起,跨平台文件处理是一个关键的考虑因素。在不同的操作系统(Linux、macOS、Windows)中,文件路径处理、权限甚至区分大小写都可能有所不同。

路径处理

在 .NET 中,并帮助标准化文件路径构造:Path.CombinePath.DirectorySeparatorChar

string path = Path.Combine(Environment.CurrentDirectory, "data", "log.txt");

这可确保正确构建您的路径,无论应用程序是在 Linux、macOS 还是 Windows 上运行。

记得

  • 异步文件 I/O 对于现代响应式应用程序至关重要。它减少了阻塞并提高吞吐量,尤其是对于大型文件或基于网络的文件。

  • 内存映射文件提供了一种处理超大型数据集的有效方法,可实现随机访问和直接内存操作。

  • Streams 允许进行精细的控制和缓冲,使其成为大型文件或性能敏感型应用程序的理想选择。

  • 跨平台注意事项:始终使用与平台无关的方法,例如避免跨不同 OS 环境的文件路径问题。Path.Combine

在 C# 中处理文件和流是任何使用数据密集型应用程序的开发人员的基本技能。通过利用异步文件操作、流和现代 C# 功能(如内存映射文件),您可以高效处理大型数据集,而不会影响性能。


推荐阅读:
在 .NET 中使用日期和时间(针对 .NET 8/9 更新)
2款.NET开源且高效的代码格式化工具
一个适用于 ASP.NET Core 的轻量级插件框架
面试常考:彻底搞清楚C#垃圾回收机制(GC)
在 .NET 和 Python 中创建了相同的 API — 哪个性能更好?
6款支持C#语言的AI辅助编程工具,开发效率提升利器!

点击下方卡片关注DotNet NB

一起交流学习

▲ 点击上方卡片关注DotNet NB,一起交流学习

请在公众号后台

回复 【路线图】获取.NET 2024开发者路线
回复 【原创内容】获取公众号原创内容
回复 【峰会视频】获取.NET Conf大会视频
回复 【个人简介】获取作者个人简介
回复 【年终总结】获取作者年终回顾
回复 加群加入DotNet NB 交流学习群

长按识别下方二维码,或点击阅读原文。和我一起,交流学习,分享心得。

DotNet NB
.NET 技术学习分享,社区热点分享,专注为 .NET 社区做贡献,愿我们互相交流学习,共同推动社区发展
 最新文章