C# 文件的输入与输出

日之朝矣

I/O,其实也就是Input/Output输入与输出

I/O类

System.IO 命名空间有各种不同的类,用于执行各种文件操作,如创建和删除文件、读取或写入文件,关闭文件等。

下表列出了一些 System.IO 命名空间中常用的非抽象类:

I/O 类描述
BinaryReader从二进制流读取原始数据。
BinaryWriter以二进制格式写入原始数据。
BufferedStream字节流的临时存储。
Directory有助于操作目录结构。
DirectoryInfo用于对目录执行操作。
DriveInfo提供驱动器的信息。
File有助于处理文件。
FileInfo用于对文件执行操作。
FileStream用于文件中任何位置的读写。
MemoryStream用于随机访问存储在内存中的数据流。
Path对路径信息执行操作。
StreamReader用于从字节流中读取字符。
StreamWriter用于向一个流中写入字符。
StringReader用于读取字符串缓冲区。
StringWriter用于写入字符串缓冲区。

Path

Path是一个静态类,对包含文件或目录路径信息的 String 实例执行操作。 这些操作是以跨平台的方式执行的。

下面是它能够调用的静态方法

方法描述
ChangeExtension(String, String)更改路径字符串的扩展名。
Combine(String, String)将两个字符串组合成一个路径。
Combine(String, String, String)将三个字符串组合成一个路径。
Combine(String, String, String, String)将四个字符串组合成一个路径。
Combine(String[])将字符串数组组合成一个路径。
GetDirectoryName(String)返回指定路径的目录信息。
GetExtension(String)返回指定路径字符串的扩展名(包括句点“.”)。
GetFileName(String)返回指定路径字符串的文件名和扩展名。
GetFileNameWithoutExtension(String)返回不具有扩展名的指定路径字符串的文件名。
GetFullPath(String)返回指定路径字符串的绝对路径。
GetInvalidFileNameChars()获取包含不允许在文件名中使用的字符的数组。
GetInvalidPathChars()获取包含不允许在路径名中使用的字符的数组。
GetPathRoot(String)从指定字符串包含的路径中获取根目录信息。
GetRandomFileName()返回随机文件夹名或文件名。
GetTempFileName()在磁盘上创建一个唯一命名的零字节临时文件,并返回该文件的完整路径。
GetTempPath()返回当前用户的临时文件夹的路径。
HasExtension(String)确定路径是否包括文件扩展名。
IsPathRooted(String)返回一个值,该值指示指定的路径字符串是否包含根。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
string str = @"C:\mydir\myfile.com.extension";
Console.WriteLine("更改扩展名为空: " + Path.ChangeExtension(str,""));
Console.WriteLine("更改扩展名为.exe: " + Path.ChangeExtension(str,".exe") + "\n");

Console.WriteLine("组合路径Path.Combine(\"c:\\\",\"mydir\"): " + Path.Combine(@"C:\","mydir"));
Console.WriteLine("组合路径Path.Combine(\"c:\\\",\"mydir\",\"myfile.com.extension\"): " + Path.Combine(@"C:\","mydir","myfile.com.extension")+ "\n");

Console.WriteLine("返回指定路径的目录信息: " + Path.GetDirectoryName(str)+ "\n");

Console.WriteLine("返回指定路径字符串的扩展名: " + Path.GetExtension(str) + "\n");

Console.WriteLine("返回指定路径字符串的文件名和扩展名: " + Path.GetFileName(str) + "\n");

Console.WriteLine("返回不含扩展名的指定路径字符串的文件名: " + Path.GetFileNameWithoutExtension(str) + "\n");

Console.WriteLine("返回指定路径字符串的绝对路径: " + Path.GetFullPath(@".\") + "\n");

// Console.WriteLine("获取包含不允许在文件名中使用的字符数组: " + string.Join(" ", Path.GetInvalidFileNameChars()) + "\n");
//
// Console.WriteLine("获取包含不允许在路径中使用的字符数组: " + string.Join(" ", Path.GetInvalidPathChars()) + "\n");

Console.WriteLine("从指定字符串包含的路径中获取根目录信息: " + Path.GetPathRoot(str) + "\n");

Console.WriteLine("返回随机文件夹名或文件名: " + Path.GetRandomFileName() + "\n");

// Console.WriteLine("从磁盘上创建一个唯一命名的零字节临时文件,并返回完整路径: " + Path.GetTempFileName() + "\n");

Console.WriteLine("返回当前用户的临时文件夹的路径: " + Path.GetTempPath() + "\n");

Console.WriteLine("确定路径是否包括文件扩展名: " + Path.HasExtension(str) + "\n");

Console.WriteLine("确定路径字符串是否包含根: " + Path.IsPathRooted(str) + "\n");

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
更改扩展名为空: C:\mydir\myfile.com.
更改扩展名为.ddd: C:\mydir\myfile.com.ddd

C:\mydir\myfile.com.extension


组合路径Path.Combine("c:\","mydir"): C:\mydir
返回指定路径的目录信息: C:\mydirr","myfile.com.extension"): C:\mydir\myfile.com.extension

返回指定路径字符串的扩展名: .extension

返回指定路径字符串的文件名和扩展名: myfile.com.extension

返回不含扩展名的指定路径字符串的文件名: myfile.com

返回指定路径字符串的绝对路径: D:\code\c#\20240803\测试控制台\bin\Debug\

指定字符串包含的路径中获取根目录信息: C:\

返回随机文件夹名或文件名: y5ceuojd.wbm

返回当前用户的临时文件夹的路径: C:\Users\马洪振\AppData\Local\Temp\

确定路径是否包括文件扩展名: True

确定路径字符串是否包含根: True

File

提供用于创建、复制、删除、移动和打开单个文件的静态方法,并有助于创建 FileStream 对象

File 类的主要功能和常用方法

以下是 File 类的主要功能和一些常用方法的详细介绍:

创建和写入文件

创建文件并写入文本:

1
2
3
4
5
using System.IO;

string path = @"C:\example.txt";
string content = "Hello, World!";
File.WriteAllText(path, content);

创建文件并写入多行文本:

1
2
string[] lines = { "First line", "Second line", "Third line" };
File.WriteAllLines(path, lines);

创建文件并写入字节:

1
2
byte[] data = { 0x0, 0x1, 0x2 };
File.WriteAllBytes(path, data);

读取文件

读取文件中的所有文本:

1
string content = File.ReadAllText(path);

读取文件中的所有行:

1
string[] lines = File.ReadAllLines(path);

读取文件中的字节:

1
byte[] data = File.ReadAllBytes(path);

检查文件的存在

检查文件是否存在:

1
2
3
4
5
6
7
8
if (File.Exists(path))
{
Console.WriteLine("File exists.");
}
else
{
Console.WriteLine("File does not exist.");
}

删除文件

删除文件:

1
File.Delete(path);

复制和移动文件

复制文件:

1
2
string destPath = @"C:\example_copy.txt";
File.Copy(path, destPath);

复制文件并覆盖现有文件:

1
File.Copy(path, destPath, true);

移动文件:

1
2
string newPath = @"C:\new_example.txt";
File.Move(path, newPath);

修改文件名:同样使用移动文件的方式,只需要修改newpath中的文件名即可

打开文件流

打开文件流进行读取:

1
2
3
4
using (FileStream fs = File.OpenRead(path))
{
// 读取操作
}

打开文件流进行写入:

1
2
3
4
using (FileStream fs = File.OpenWrite(path))
{
// 写入操作
}

打开或创建文件流进行读写:

1
2
3
4
using (FileStream fs = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
// 读写操作
}

追加文本

追加文本到文件末尾:

1
2
string appendText = "This is an additional line.";
File.AppendAllText(path, appendText);

获取文件信息

获取文件创建时间:

1
DateTime creationTime = File.GetCreationTime(path);

获取文件最后修改时间:

1
DateTime lastWriteTime = File.GetLastWriteTime(path);

获取文件最后访问时间:

1
DateTime lastAccessTime = File.GetLastAccessTime(path);

FileInfo

FileInfo 类封装了文件系统上的一个文件,并提供了获取文件属性和执行文件操作的功能。与 File 类不同的是,FileInfo 是面向对象的,可以保存文件的状态信息,并且适合频繁操作同一个文件的场景。

创建 FileInfo 对象

要使用 FileInfo 类,需要首先创建一个 FileInfo 对象,指向某个具体的文件。

1
2
3
using System.IO;

FileInfo fileInfo = new FileInfo(@"C:\example.txt");

属性

FileInfo 类提供了一些常用的属性,用于获取文件的各种信息:

  • Name: 获取文件的名称(不包括路径)。

    1
    string fileName = fileInfo.Name;
  • FullName: 获取文件的全路径。

    1
    string fullPath = fileInfo.FullName;
  • Directory: 获取一个 DirectoryInfo对象,表示文件的目录。

    1
    DirectoryInfo directory = fileInfo.Directory;
  • DirectoryName: 获取文件目录的路径。

    1
    string directoryName = fileInfo.DirectoryName;
  • Length: 获取文件的大小(以字节为单位)。

    1
    long fileSize = fileInfo.Length;
  • CreationTime: 获取或设置文件的创建时间。

    1
    DateTime creationTime = fileInfo.CreationTime;
  • LastAccessTime: 获取或设置文件的最后访问时间。

    1
    DateTime lastAccessTime = fileInfo.LastAccessTime;
  • LastWriteTime: 获取或设置文件的最后修改时间。

    1
    DateTime lastWriteTime = fileInfo.LastWriteTime;
  • Attributes: 获取或设置文件的属性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // FileAttributes 是一组枚举,枚举文件的属性如:系统文件,隐藏文件,压缩文件,临时文件等
    // 枚举值是2的n次幂,因此可以使用`|`来包含多个枚举而不冲突
    // 判断是否含有某种枚举类型时,使用按位与`&`来判断。
    FileAttributes attributes = fileInfo.Attributes;

    // 判断是否是文件夹
    if ((attributes & FileAttributes.Directory) == FileAttributes.Directory){
    Console.WriteLine("这是一个文件夹");
    }
  • Exists: 检查文件是否存在。

    1
    bool exists = fileInfo.Exists;
  • IsReadOnly: 获取或设置文件是否只读。

    1
    bool isReadOnly = fileInfo.IsReadOnly;

方法

FileInfo 类提供了许多方法来执行文件操作:

  • Create: 创建文件。

    1
    fileInfo.Create().Close();
  • Delete: 删除文件。

    1
    fileInfo.Delete();
  • CopyTo: 复制文件到指定路径,可以选择是否覆盖现有文件。

    1
    2
    3
    string destPath = @"C:\example_copy.txt";
    fileInfo.CopyTo(destPath);
    fileInfo.CopyTo(destPath, true); // 覆盖现有文件
  • MoveTo: 移动文件到新路径,可以用于重命名文件。

    1
    2
    string newPath = @"C:\new_example.txt";
    fileInfo.MoveTo(newPath);
  • OpenRead: 打开文件以进行读取操作,返回 FileStream对象。

    1
    2
    3
    4
    using (FileStream fs = fileInfo.OpenRead())
    {
    // 读取操作
    }
  • OpenWrite: 打开文件以进行写入操作,返回 FileStream对象。

    1
    2
    3
    4
    using (FileStream fs = fileInfo.OpenWrite())
    {
    // 写入操作
    }
  • Open: 以指定的模式和访问权限打开文件,返回 FileStream 对象。

    1
    2
    3
    4
    using (FileStream fs = fileInfo.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite))
    {
    // 读写操作
    }
  • Refresh: 刷新 FileInfo对象的缓存。

    1
    fileInfo.Refresh();

Directory

公开用于通过目录和子目录进行创建、移动和枚举的静态方法。 此类不能被继承。

常用方法

创建目录

  • CreateDirectory: 创建所有指定路径中不存在的目录和子目录。

    1
    Directory.CreateDirectory(@"C:\example\newFolder");

删除目录

  • Delete: 删除指定的目录。如果目录中包含文件或子目录,可以通过指定参数来删除它们。

    1
    2
    3
    4
    5
    // 删除空目录
    Directory.Delete(@"C:\example\newFolder");

    // 删除包含文件和子目录的目录
    Directory.Delete(@"C:\example\newFolder", true);

移动目录

  • Move: 将目录及其内容移动到新的位置。

    1
    Directory.Move(@"C:\example\oldFolder", @"C:\example\newFolder");

检查目录是否存在

  • Exists: 检查指定的目录是否存在。

    1
    bool exists = Directory.Exists(@"C:\example\newFolder");

获取目录信息

  • GetFiles: 获取指定目录中的文件列表。

    1
    string[] files = Directory.GetFiles(@"C:\example\newFolder");
  • GetDirectories: 获取指定目录中的子目录列表。

    1
    string[] directories = Directory.GetDirectories(@"C:\example\newFolder");
  • GetFileSystemEntries: 获取指定目录中的所有文件和子目录的列表。

    1
    string[] entries = Directory.GetFileSystemEntries(@"C:\example\newFolder");

获取目录属性

  • GetCreationTime: 获取目录的创建时间。

    1
    DateTime creationTime = Directory.GetCreationTime(@"C:\example\newFolder");
  • GetLastAccessTime: 获取目录的上次访问时间。

    1
    DateTime lastAccessTime = Directory.GetLastAccessTime(@"C:\example\newFolder");
  • GetLastWriteTime: 获取目录的上次写入时间。

    1
    DateTime lastWriteTime = Directory.GetLastWriteTime(@"C:\example\newFolder");

设置目录属性

  • SetCreationTime: 设置目录的创建时间。

    1
    Directory.SetCreationTime(@"C:\example\newFolder", DateTime.Now);
  • SetLastAccessTime: 设置目录的上次访问时间。

    1
    Directory.SetLastAccessTime(@"C:\example\newFolder", DateTime.Now);
  • SetLastWriteTime: 设置目录的上次写入时间。

    1
    Directory.SetLastWriteTime(@"C:\example\newFolder", DateTime.Now);

DirectoryInfo

公开用于创建、移动和枚举目录和子目录的实例方法。

创建 DirectoryInfo 对象

1
DirectoryInfo dirInfo = new DirectoryInfo(@"C:\example\newFolder");

常用属性

属性

  • FullName: 获取目录的完整路径。

    1
    string fullPath = dirInfo.FullName;
  • Name: 获取目录的名称。

    1
    string name = dirInfo.Name;
  • Parent: 获取父目录的 DirectoryInfo 对象。

    1
    DirectoryInfo parentDir = dirInfo.Parent;
  • Root: 获取根目录的 DirectoryInfo 对象。

    1
    DirectoryInfo rootDir = dirInfo.Root;

获取和设置目录属性

  • CreationTime: 获取或设置目录的创建时间。

    1
    2
    DateTime creationTime = dirInfo.CreationTime;
    dirInfo.CreationTime = DateTime.Now;
  • LastAccessTime: 获取或设置目录的上次访问时间。

    1
    2
    DateTime lastAccessTime = dirInfo.LastAccessTime;
    dirInfo.LastAccessTime = DateTime.Now;
  • LastWriteTime: 获取或设置目录的上次写入时间。

    1
    2
    DateTime lastWriteTime = dirInfo.LastWriteTime;
    dirInfo.LastWriteTime = DateTime.Now;

常用方法

创建目录

  • Create: 创建目录。

    1
    dirInfo.Create();

删除目录

  • Delete: 删除目录,可以选择是否递归删除所有内容。

    1
    dirInfo.Delete(true); // true 表示递归删除子目录和文件

移动目录

  • MoveTo: 将目录及其内容移动到新的位置。

    1
    dirInfo.MoveTo(@"C:\example\newLocation");

获取目录信息

  • GetFiles: 获取目录中的文件信息。

    1
    FileInfo[] files = dirInfo.GetFiles();
  • GetDirectories: 获取目录中的子目录信息。

    1
    DirectoryInfo[] directories = dirInfo.GetDirectories();
  • GetFileSystemInfos: 获取目录中的文件和子目录信息。

    1
    FileSystemInfo[] entries = dirInfo.GetFileSystemInfos();

DriveInfo

提供对有关驱动器的信息的访问。

创建 DriveInfo 对象

  • DriveInfo(string driveName): 使用指定的驱动器名称初始化 DriveInfo 类的新实例。

    1
    DriveInfo driveInfo = new DriveInfo("C");

主要属性

  • AvailableFreeSpace: 获取驱动器上的可用空闲空间(以字节为单位)。

    1
    long availableFreeSpace = driveInfo.AvailableFreeSpace;
  • DriveFormat: 获取驱动器的文件系统格式(如 NTFS、FAT32)。

    1
    string driveFormat = driveInfo.DriveFormat;
  • DriveType: 获取驱动器的类型(如固定、可移动、CD-ROM、网络驱动器等)。

    1
    DriveType driveType = driveInfo.DriveType;
  • IsReady: 获取一个值,指示驱动器是否已准备好(即是否已插入并可访问)。

    1
    bool isReady = driveInfo.IsReady;
  • Name: 获取驱动器的名称。

    1
    string driveName = driveInfo.Name;
  • RootDirectory: 获取驱动器的根目录。

    1
    DirectoryInfo rootDirectory = driveInfo.RootDirectory;
  • TotalFreeSpace: 获取驱动器上的总空闲空间(以字节为单位)。

    1
    long totalFreeSpace = driveInfo.TotalFreeSpace;
  • TotalSize: 获取驱动器的总大小(以字节为单位)。

    1
    long totalSize = driveInfo.TotalSize;
  • VolumeLabel: 获取或设置驱动器的卷标。

    1
    2
    string volumeLabel = driveInfo.VolumeLabel;
    driveInfo.VolumeLabel = "New Volume";

主要方法

  • DriveInfo.GetDrives(): 获取计算机上所有驱动器的数组。

    1
    DriveInfo[] allDrives = DriveInfo.GetDrives();

流式操作

流式操作(streaming operation)通常指的是对数据进行逐块(逐行、逐字节等)处理的操作方式

流式操作的优势

  • 低内存消耗:逐块处理数据而不是一次性加载全部数据,可以显著降低内存使用。
  • 处理大型数据集:适合处理无法一次性加载到内存中的大型数据集,如大文件或网络数据流。
  • 即时处理:在数据尚未完全获取或处理完成之前就可以开始处理部分数据,提高响应速度

流式读写操作

  • 流式读操作:按块读取数据,处理每一块数据后再读取下一块。
  • 流式写操作:按块写入数据,每次写入一块数据。

缓冲区

写入

缓冲区在写入时的作用:

  • 提高性能: 缓冲区用于暂时存储写入操作的数据,直到缓冲区满或到达特定的条件时才将数据实际写入到目标(如文件或网络)。这样可以减少对目标的访问频率,优化性能,因为直接对目标设备的操作(如磁盘写入)通常比内存操作慢得多。
  • 减少操作次数: 将多个小的写入操作合并成一个较大的写入操作可以减少磁盘I/O次数,提高效率。缓冲区中的数据被积累后,一次性写入可以减少对目标设备的访问,从而提高性能。

缓冲区的行为:

  • 自动刷新: 当缓冲区填满时,系统会自动将缓冲区中的数据写入到目标设备。这种自动刷新机制确保了数据及时被处理,而不需要每次写入都显式调用刷新操作。
  • 显式刷新: 有些情况下,程序员可以显式调用刷新操作(如 Flush 方法),以确保缓冲区中的数据被立即写入到目标设备。这对于需要保证数据及时写入的场景(如日志记录)尤其重要。

一些流(如StreamWriter)的实现会在 Dispose()Close() 方法中自动调用 Flush(),以确保所有数据被正确写入,即使你没有显式调用 Flush()

读取

缓冲区在读取时的作用:

  • 提高性能: 缓冲区用于暂时存储从目标(如文件或网络)读取的数据,以减少对目标的访问次数。读取操作从目标中获取一块数据到缓冲区中,然后从缓冲区中读取数据。这样可以减少对目标的频繁访问,提高读取效率。
  • 减少延迟: 读取缓冲区可以减少因目标设备(如磁盘)速度慢而导致的延迟。通过在内存中缓存数据,可以更快地响应读取请求。

缓冲区的行为:

  • 按需填充: 当读取操作请求数据时,缓冲区会从目标设备中获取一块数据。如果缓冲区中的数据不足,系统会自动从目标设备中读取更多数据来填充缓冲区。这种机制确保了在读取数据时可以尽可能快速地获取数据。
  • 读取操作: 读取操作从缓冲区中获取数据,而不是直接访问目标设备。这使得读取操作更加高效,因为访问内存比访问磁盘要快得多。

using语句

using 语句是 C# 中用于简化资源管理的一种语法结构。它确保在使用完资源后自动释放资源,常用于处理实现了 IDisposable 接口的对象。

1
2
3
4
using(var resource = new ResourceType())
{
// 使用资源
}

当离开 using 语句块时,资源会自动调用其 Dispose 方法,释放资源。

FileStream

为文件提供 Stream,既支持同步读写操作,也支持异步读写操作。

创建 FileStream 对象

FileStream 对象可以通过多种方式创建,常见的构造函数有:

1
2
3
4
5
6
7
8
9
10
using System.IO;

// 通过文件路径、文件模式创建 FileStream 对象
FileStream fileStream = new FileStream("example.txt", FileMode.OpenOrCreate);

// 通过文件路径、文件模式和文件访问权限创建 FileStream 对象
FileStream fileStream = new FileStream("example.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite);

// 通过文件路径、文件模式、文件访问权限和文件共享模式创建 FileStream 对象
FileStream fileStream = new FileStream("example.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);

常用属性

  • CanRead: 获取一个值,指示当前流是否支持读取。

    1
    bool canRead = fileStream.CanRead;
  • CanWrite: 获取一个值,指示当前流是否支持写入。

    1
    bool canWrite = fileStream.CanWrite;
  • CanSeek: 获取一个值,指示当前流是否支持查找。

    1
    bool canSeek = fileStream.CanSeek;
  • Length: 获取流的长度(以字节为单位)。

    1
    long length = fileStream.Length;
  • Position: 获取或设置流中的当前位置。

    1
    long position = fileStream.Position;

常用方法

  • Read: 从当前流中读取字节块并将数据写入缓冲区。

    1
    2
    byte[] buffer = new byte[1024];
    int bytesRead = fileStream.Read(buffer, 0, buffer.Length);
  • Write: 将字节块写入当前流。

    1
    2
    byte[] data = Encoding.UTF8.GetBytes("Hello, FileStream!");
    fileStream.Write(buffer, 0, buffer.Length);
  • Seek: 在流中查找指定位置。

    1
    fileStream.Seek(0, SeekOrigin.Begin);
  • SetLength: 设置当前流的长度。

    1
    fileStream.SetLength(2048);
  • Flush: 清除缓冲区的内容并将其写入基础设备。

    1
    fileStream.Flush();
  • Close: 关闭当前流并释放与之关联的所有资源。

    1
    fileStream.Close();

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void Main(string[] args)
{
// 创建并写入文件
using (FileStream fileStream = new FileStream("example.txt", FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] data = Encoding.UTF8.GetBytes("Ciallo~(∠·ω< )⌒★");
fileStream.Write(data, 0, data.Length);
}
Console.ReadLine();
// 读取文件
using (FileStream fileStream = new FileStream("example.txt", FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[1024];
int bytesRead = fileStream.Read(buffer, 0, buffer.Length);
string content = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine("File content: " + content);
}
}

StreamWriter

StreamWriter 是用于写入字符到流中的一个类。常用于写入文本到文件或者其他流对象中。StreamWriter 使用特定的编码来将字符写入到基础流中。

创建 StreamWirter 对象

StreamWriter 提供多个构造函数,允许你指定流、文件路径、编码等参数。

1
2
3
4
5
6
7
8
9
10
11
// 1. 使用文件路径创建 StreamWriter
StreamWriter writer = new StreamWriter(string path);

// 2. 使用文件路径和编码创建 StreamWriter
StreamWriter writer = new StreamWriter(string path, bool append, Encoding encoding);

// 3. 使用 Stream 创建 StreamWriter
StreamWriter writer = new StreamWriter(Stream stream);

// 4. 使用 Stream 和编码创建 StreamWriter
StreamWriter writer = new StreamWriter(Stream stream, Encoding encoding);

常用属性

  • AutoFlush:获取或设置一个值,该值指示 StreamWriter 是否在每次写入操作后自动刷新缓冲区。

    1
    writer.AutoFlush = true;
  • Encoding:获取当前 StreamWriter 使用的编码。

    1
    Encoding encoding = writer.Encoding;

常用方法

  • Write:写入一个或多个字符。

    1
    writer.Write("Hello, World!");
  • WriteLine:写入一个或多个字符并追加换行符。

    1
    writer.WriteLine("Hello, World!");
  • Flush:将缓冲区中的数据刷新到基础流。

    1
    writer.Flush();
  • Close:关闭 StreamWriter 和基础流。

    1
    writer.Close();
  • Dispose:释放 StreamWriter 使用的所有资源。

    1
    writer.Dispose();

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
string path = "example.txt";
string text = "Hello, World!";

// 使用 using 语句创建 StreamWriter
using (StreamWriter writer = new StreamWriter(path, false, Encoding.UTF8))
{
writer.WriteLine(text);
}

// 验证写入结果
string readText = File.ReadAllText(path);
Console.WriteLine(readText); // 输出 "Hello, World!"

StreamReader

StreamReader 是用于读取字符数据的类,主要从字节流中读取文本。

创建 StreamReader 对象

StreamReader 提供了多种构造函数,允许从不同的输入源读取文本:

  1. 从文件读取

    1
    StreamReader reader = new StreamReader("path/to/file.txt");

    直接从指定路径的文件中读取文本。

  2. 从流读取

    1
    2
    FileStream fs = new FileStream("path/to/file.txt", FileMode.Open);
    StreamReader reader = new StreamReader(fs);

    从已有的 Stream 对象(如 FileStream)中读取文本。

  3. 指定编码

    1
    StreamReader reader = new StreamReader("path/to/file.txt", Encoding.UTF8);

    从指定路径的文件中读取文本,并指定文本的编码格式。

  4. 其他参数

    1
    StreamReader reader = new StreamReader("path/to/file.txt", Encoding.UTF8, true, 1024);

    允许指定缓冲区大小和其他参数。

常用方法

  1. Read():

    1
    int data = reader.Read();

    从输入流中读取下一个字符,并返回其整数表示。如果到达流的末尾,则返回 -1。

  2. ReadLine():

    1
    string line = reader.ReadLine();

    读取流中的一行字符,并返回作为字符串。如果到达流的末尾,则返回 null

  3. ReadToEnd():

    1
    string allText = reader.ReadToEnd();

    从当前位置开始读取流中的所有字符,直到流的末尾,并将其作为一个字符串返回。

  4. Peek():

    1
    int nextChar = reader.Peek();

    返回下一个字符的整数表示,但不从输入流中移除该字符。如果到达流的末尾,则返回 -1。

  5. Close():

    1
    reader.Close();

    关闭 StreamReader 对象和基础流。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
string path = "example.txt";

// 确保文件存在
if (File.Exists(path))
{
// 使用 StreamReader 从文件中读取数据
using (StreamReader reader = new StreamReader(path))
{
// 读取并输出文件内容
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
} // using 语句结束时会自动调用 reader.Close()
}
else
{
Console.WriteLine("文件不存在!");
}

MemoryStream

MemoryStream 是 .NET 中用于在内存中读写数据的一种流类型。它继承自 Stream 类,与其他流类型(如 FileStream)不同,MemoryStream 不会将数据写入磁盘或其他存储设备,而是将数据保存在内存中。

特性

  • 内存存储: 数据被保存在内存中的一个字节数组里,不涉及任何外部存储。
  • 高效性: 由于数据在内存中,读写速度非常快,但受限于可用内存的大小。
  • 灵活性: 支持动态增长,可以自动调整大小来适应数据的增加。

使用场景

  • 临时存储: 适用于需要临时存储数据的场景,例如中间数据的缓冲。
  • 数据处理: 可以用于处理和操作字节数组,避免多次磁盘读写。
  • 序列化: 适用于序列化和反序列化操作,方便在内存中处理对象的二进制表示。

该类型实现了IDisposasble接口,但实际上并没有任何资源需要释放, 这意味着无需通过直接调用 Dispose() 或使用 using 对其进行处理。

创建 MemoryStream 实例

  • 构造函数:
    • MemoryStream():使用初始化为零的可扩展容量初始化 MemoryStream 类的新实例。
    • MemoryStream(byte[] buffer):基于指定的字节数组初始化 MemoryStream 类的无法调整大小的新实例。
    • MemoryStream(int capacity):创建一个具有指定初始容量的可扩展容量初始化 MemoryStream 类的新实例。
1
2
3
4
5
MemoryStream ms1 = new MemoryStream();

MemoryStream ms2 = new MemoryStream(new byte[512]);

MemoryStream ms3 = new MemoryStream(512);

常用属性

  • Capacity:获取或设置当前流的容量。
  • Length:获取流的长度(即字节数)。
  • Position:获取或设置当前流中的位置。

常用方法

  • Read(byte[] buffer, int offset, int count):从当前流中读取数据。
  • Write(byte[] buffer, int offset, int count):向当前流中写入数据。
  • ToArray():将流的内容写入字节数组。
  • WriteTo(Stream stream):将当前内存流的内容写入另一个流中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
MemoryStream ms = new MemoryStream(300);
string s = "I am the bone of my sword. \nSteel is my body,and fire is my blood. \nI have created over a thousand blades. \nUnknown to death. \nNor known to life. \nHave withstood pain to create many weapons. \nYet those hands will never hold anything.\nSo as I pray, Unlimited Blade Works.";
byte[] bytes = Encoding.UTF8.GetBytes(s);
ms.Write(bytes,0,bytes.Length);

// 因为刚才的写入,现在Position在尾部,需要改回0
ms.Position = 0;
byte[] readBytes = new byte[20];
ms.Read(readBytes,0,readBytes.Length);
Console.WriteLine(Encoding.UTF8.GetString(readBytes) + "\n"); // I am the bone of my

// ToArray后输出
byte[] toArrayBytes = ms.ToArray();
Console.WriteLine(Encoding.UTF8.GetString(toArrayBytes) + "\n"); // 结果和字符串s一致

// 将ms内容全部写入ms2
MemoryStream ms2 = new MemoryStream(300);
ms.WriteTo(ms2);
Console.WriteLine(Encoding.UTF8.GetString(ms2.ToArray())); // 结果和字符串s一致

内部工作原理

  • 内存管理: MemoryStream 使用一个内部字节数组来存储数据。默认情况下,这个数组会根据需要动态扩展。
  • 自动调整大小: 当写入的数据超过当前容量时,MemoryStream 会自动增加内部数组的大小,以适应更多的数据。
  • 高效读写: 由于数据存储在内存中,读写操作非常快,适合高性能需求的场景。

注意事项

  • 内存限制: 虽然 MemoryStream 很高效,但它受限于系统的可用内存。对于非常大的数据集,使用 MemoryStream 可能会导致内存不足的问题。
  • 数据持久性: 数据仅存储在内存中,程序结束或 MemoryStream 对象被释放后,数据将会丢失。如果需要持久化数据,需要将数据保存到磁盘或其他存储介质。

StringWriter

该类型实现了IDisposasble接口,但实际上并没有任何资源需要释放, 这意味着无需通过直接调用 Dispose() 或使用 using 对其进行处理。

因为SringWriter实际上是对StringBuilder的封装,所以Flush()方法也是无用的。

创建 StringWriter 对象

1
2
3
4
StringWriter sw1 = new StringWriter();

// 使用 StringBuilder 创建StringWriter
StringWriter sw2 = new StringWriter( new StringBuilder("Ciallo~(∠·ω< )⌒★") );

常用属性

Encoding:获取写入输出的 Encoding

1
Encoding encoding = sw1.Encoding;

常用方法

  • write():将数据写入文本流

    1
    sw.Write("Ciallo~(∠·ω< )⌒★\n"); // 继承自TextWriter,与StreamWriter的该方法使用方式一致
  • writeLine():将数据写入文本流,后跟行终止符。

    1
    sw.WriteLine("Ciallo~(∠·ω< )⌒★\n");// 继承自TextWriter,与StreamWriter的该方法使用方式一致
  • GetStringBuilder():返回基础 StringBuilder

    1
    StringBuilder sb = sw.GetStringBuilder();
  • ToString():返回包含迄今为止写入到当前 StringWriter 中的字符的字符串。

与StringBuilder的关系

StringWriterStringReader 是基于 StringBuilder 进行操作的类。它们提供了一种方便的方式来处理字符串数据,分别用于写入和读取字符串数据。由于继承自 TextWriter,其操作可能会比直接使用 StringBuilder 稍微多一些性能开销。

使用场景:

  • **使用 StringWriter**:当需要与其他 TextWriter API 集成或进行逐行、逐块数据操作时。
  • **使用 StringBuilder**:当需要进行高效的字符串拼接、插入、删除等操作时。

StringReader

创建 StringReader 实例

1
2
// 初始化从指定字符串进行读取的 StringReader 类的新实例。
StringReader sr = new StringReader(String);

常用方法

  • Read():读取输入字符串中的下一个字符并将该字符的位置提升一个字符。
  • ReadLine():从当前字符串中读取一行字符并将数据作为字符串返回。
  • ReadBlock():从当前流中读取字符并将数据写入缓冲区。
  • ReadToEnd():读取从当前位置到字符串的结尾的所有字符并将它们作为单个字符串返回。
  • Peek():返回下一个可用字符,但不使用它。

参考内容

参考内容:

Microsoft Learn .NET API

菜鸟教程 - C# 教程

ChatGPT

  • 标题: C# 文件的输入与输出
  • 作者: 日之朝矣
  • 创建于 : 2024-08-03 21:11:52
  • 更新于 : 2024-08-18 09:25:27
  • 链接: https://blog.rzzy.fun/2024/08/03/csharp-file-input-output/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论