CodeGym /课程 /C# SELF /在C#中操作文件: System.IO.File

在C#中操作文件: System.IO.File

C# SELF
第 35 级 , 课程 2
可用

1. File类:简单又高效

System.IO.File类就是“一个操作一个方法”的理念。它提供了一堆高级方法,把和操作系统打交道的复杂逻辑都封装好了,让你专注于自己的任务。

File类到底能干啥?

  • 把文本或任意二进制数据写进文件。
  • 读取文件内容,可以一次性读完,也可以按行读(针对文本文件)。
  • 复制、删除和重命名已有文件。
  • 检查文件是否存在,只要给定路径就行。
  • 操作文件属性(比如创建日期、大小——通过相关类)。

为啥这么方便?
所有File的方法都是静态的。也就是说,你不用new对象,直接调用方法就行,比如:

File.WriteAllText("data.txt", "你好,文件!");

这种风格很像用Console类:简单明了,不用“new”,不用管流啥的。

如果你想要一个通用又顺手的文件操作工具,System.IO.File就是那个“瑞士军刀”。它有一堆方法,能读、写、复制、删除文件,基本操作全都能搞定。

怎么用File

File类的所有方法都是静态的,所以不用new对象,直接写File.啥啥()就行。这和Console.WriteLine()很像,只不过这次是操作文件。

string text = "你好,世界!";
string path = "hello.txt";

// 把文本写进文件(如果文件已存在会覆盖!)
File.WriteAllText(path, text);

// 一次性读出整个文件内容
string readText = File.ReadAllText(path);

Console.WriteLine("文件内容: " + readText);

小技巧:如果你老是用ReadAllTextWriteAllText,十次八次都这样,估计你用错方法了——这些方法适合简单场景和小文件。

File类常用方法

方法 说明
File.Exists(path)
检查指定路径下的文件是否存在
File.Delete(path)
删除文件
File.Copy(src, dest)
复制文件
File.Move(src, dest)
移动(或重命名)文件
File.ReadAllText(path)
把整个文件读成一个字符串
File.WriteAllText(path, t)
把字符串写进文件(如果已存在会覆盖)
File.ReadAllLines(path)
把文件的每一行读成字符串数组
File.WriteAllLines(path, a)
把字符串数组写进文件(每个元素一行)
File.ReadAllBytes(path)
把文件所有字节读成字节数组
File.WriteAllBytes(path, b)
把字节数组写进文件

2. 读写文本文件

下面咱们用实际例子看看File类最常用的方法。用这些方法前,记得在代码文件开头加上using System.IO;

using System;
using System.IO; // 用File类必须加
using System.Linq; // 有时ReadLines会用到

一次性读整个文件

这是最常见的需求之一:把文本数据保存到文件,或者再读回来。

写一行(或全部文本)进文件:File.WriteAllText() 这个方法会在指定路径创建新文件。如果同名文件已存在,内容会被新文本完全覆盖

string myFilePath = "notes.txt";
string textContent = "今天是学习C#的好日子。\n别忘了做作业。";

// 把所有文本写进文件
File.WriteAllText(myFilePath, textContent); 
Console.WriteLine($"文件 '{myFilePath}' 已成功创建/覆盖。");

一次性把文件读成一个字符串:File.ReadAllText() 这个方法会把整个文本文件内容读出来,返回一个大字符串。

string myFilePath = "notes.txt";

// 注意:读文件前一定要先检查文件是否存在,避免出错!
if (File.Exists(myFilePath))
{
    string content = File.ReadAllText(myFilePath);
    Console.WriteLine($"文件 '{myFilePath}' 的内容:\n{content}");
}
else
{
    Console.WriteLine($"错误: 文件 '{myFilePath}' 没找到,无法读取。");
}

重要提示:File.WriteAllText()File.ReadAllText()很适合小文件和中等大小文件(比如笔记、配置文件、短日志)。但如果文件很大(几十、几百兆甚至几个G),一次性把内容读进内存会占用大量内存,甚至可能导致OutOfMemoryException。这种情况要用更高效的方法(后面会讲)。

3. 按行读取文件

当你要处理很大的文本文件,比如巨大的CSV报表、长日志文件或文本数据库时,一次性把整个文件读进内存(也就是把整个文件从磁盘加载到电脑内存)就不划算,甚至根本不行。这时候就要用File.ReadLines()

File.ReadLines(path)——这个方法不会一次性加载整个文件。它返回一个特殊的集合(IEnumerable<string>),让你按需一行一行地读。这叫“懒加载”或“流式读取”。

string logFilePath = "application.log";
string text = """
INFO: 应用已启动。
WARN: 在模块X发现问题。
ERROR: 网络严重错误。
INFO: 应用已停止。    
""";    
    
// 假设我们有一个多行日志文件
File.WriteAllText(logFilePath, text);

// 每次循环只读并处理一行
foreach (string line in File.ReadLines(logFilePath))
    Console.WriteLine($"- {line}");

优点:文件是分段读取和处理的,处理大文件时内存占用很低。

File.ReadLines()File.ReadAllLines()的区别:

  • File.ReadAllLines(path)一次性把所有行都读进内存,返回string[]数组。适合你需要同时访问所有行(比如排序、查找)。
  • File.ReadLines(path):返回IEnumerable<string>,可以一行一行遍历。只有你要下一行时才会去读,非常适合超大文件,不用全读进内存。

4. 写字符串数组进文件

有时候你有一堆字符串(比如某个集合里的),想把每个字符串作为一行写进文件。

File.WriteAllLines(path, contents)

这个方法接收一个字符串数组(或任何字符串集合),每个字符串写成文件的一行。和WriteAllText()一样,如果文件已存在会覆盖。

string membersFilePath = "participants.txt";
string[] names = { "伊万·彼得罗夫", "玛丽亚·西多罗娃", "谢尔盖·科兹洛夫", "安娜·库兹涅佐娃" };

// 数组里的每个字符串都会写成文件的一行
File.WriteAllLines(membersFilePath, names);

// 检查内容
Console.WriteLine($"'{membersFilePath}' 的内容:");
foreach (string line in File.ReadLines(membersFilePath))
    Console.WriteLine($"- {line}");

5. 操作二进制文件

File类不仅能操作文本数据,还能方便地读写二进制数据,也就是原始字节。这在保存特殊格式的设置、图片、音频或任何非文本数据时很有用。

写字节数组:File.WriteAllBytes() 创建或覆盖文件,把整个字节数组写进去。

string binaryDataPath = "settings.bin";
// 比如图片、视频、任意数据
byte[] settingsData = { 0x01, 0x0A, 0x2C, 0xFF, 0x00, 0x3F }; // 一些“原始”设置

File.WriteAllBytes(binaryDataPath, settingsData);
Console.WriteLine($"二进制数据已写入文件 '{binaryDataPath}'。");

注意:如果你用记事本打开'settings.bin',会看到乱码,因为这些字节不是文本。

读字节数组:File.ReadAllBytes() 把整个二进制文件内容读成字节数组。

if (File.Exists(binaryDataPath))
{
    byte[] readSettings = File.ReadAllBytes(binaryDataPath);
    Console.WriteLine($"从文件 '{binaryDataPath}' 读取了 {readSettings.Length} 字节。");

    Console.Write("读取的字节(十六进制显示):");
    foreach (byte b in readSettings)
        Console.Write($"{b:X2} "); // :X2把字节格式化成两位十六进制

    Console.WriteLine();
}
else
{
    Console.WriteLine($"错误: 二进制文件 '{binaryDataPath}' 没找到。");
}

这些方法很适合保存小块二进制数据,比如缓存小图片或序列化对象。

6. 管理磁盘上的文件

File类还提供了磁盘文件管理的方法。

检查文件是否存在:File.Exists(path) 如果指定路径下有文件,返回true,否则false。强烈建议在读、删或其他可能出错的操作前先用这个方法。

string myFileToCheck = "report.txt";
// 先创建一个文件做例子
File.WriteAllText(myFileToCheck, "测试报告。");

if (File.Exists(myFileToCheck))
{
    Console.WriteLine($"文件 '{myFileToCheck}' 找到了。");
    // 现在可以安全地读、删等操作
}
else
{
    Console.WriteLine($"文件 '{myFileToCheck}' 没找到。");
}

删除文件:File.Delete(path) 会把文件从磁盘彻底删掉。注意,这个操作不可恢复!

string fileToDelete = "temp_file.txt";
// 先创建一个文件做例子
File.WriteAllText(fileToDelete, "这个文件会被删除。");

if (File.Exists(fileToDelete))
{
    File.Delete(fileToDelete);
    Console.WriteLine($"文件 '{fileToDelete}' 已成功删除。");
}
else
{
    Console.WriteLine($"文件 '{fileToDelete}' 已不存在或之前就被删了。");
}

7. 文件操作中的异常

操作文件总是要和操作系统、外部资源打交道,所以很容易出错。

一定要处理异常(错误)! 文件操作时可能遇到各种问题,你的程序要能优雅地处理,而不是直接崩溃。常见情况有:

  • FileNotFoundException:你要访问的文件在指定路径下不存在。
  • DirectoryNotFoundException:你要创建或查找文件的文件夹不存在。
  • UnauthorizedAccessException:你的程序没有权限读、写或删文件/文件夹(比如系统文件或受保护区域)。
  • IOException:通用的输入/输出错误,常见于文件被别的程序占用或磁盘空间不足。

操作文件时一定要用try-catch结构。这样可以“抓住”错误,做出相应处理(比如提示用户、写日志)。

string sensitiveFilePath = "secret_data.txt";

try
{
    // 试着读一个可能不存在或没权限的文件
    string secretContent = File.ReadAllText(sensitiveFilePath);
    Console.WriteLine($"读取到的机密内容: {secretContent}");
}
catch (FileNotFoundException) // 具体错误:文件没找到
{
    Console.WriteLine($"错误: 文件 '{sensitiveFilePath}' 没找到。请确认路径正确。");
}
catch (IOException ex) // 通用输入/输出错误(比如文件被占用)
{
    Console.WriteLine($"操作文件 '{sensitiveFilePath}' 时发生IO错误: {ex.Message}");
}
catch (Exception ex) // 其他未知错误
{
    Console.WriteLine($"发生了意外错误: {ex.Message}");
}

8. 什么时候System.IO.File不是最佳选择?

虽然File类非常万能,但有些场景用它并不合适,甚至根本不行:

超大文件(几十G以上):ReadAllText()/ReadAllBytes()会把整个文件读进内存。极大文件会导致OutOfMemoryException。这种情况要用File.ReadLines()(文本)或直接用流(Streams)FileStream)分段读写。(流的内容下节课会讲。)

需要精细控制文件访问:如果你需要很细致地控制文件怎么打开(比如独占访问,别人不能读写,或者允许多个进程同时读),File类做不到。要用更底层的流类。

异步操作:高性能应用(比如Web服务器)需要文件操作不阻塞主线程,要用异步方法(比如ReadAllTextAsync()),这些也是基于流的。

部分读写:如果你只想从文件某个偏移读一段,或者往已有文件中间写数据,File类不行。得直接用文件流。

9. 总结

为啥System.IO.File这么受欢迎?

  • 简单又简洁:静态方法让你一两行代码就能搞定复杂操作,开发效率高,代码也好读。
  • 自动管理资源:File每次操作都会自动打开和关闭文件,你不用手动管理资源(手动容易出错)。
  • 日常任务全能:大多数日常文本和二进制文件操作,File都能轻松搞定。
  • 很常见:实际开发和面试都希望你会用File类的方法。
  • 复杂性都封装好了:所有方法都把和操作系统打交道的复杂逻辑藏起来,给你一个清爽易懂的接口。

常见问题和补充说明

File.ReadAllText()能读多大的文件?
几十兆(< 100 MB)的文件一般没问题。再大(几百兆、几个G),用File.ReadAllText()可能会内存爆掉(OutOfMemoryException)。这种情况建议用File.ReadLines()按行读,或者用更高级的流式处理方法,后面会讲。

能不能在不存在的文件夹里创建/写文件?
不能。File.WriteAllText()File.WriteAllBytes()等方法不会自动创建父文件夹。比如你写C:\MyAppData\Logs\current.log,但MyAppDataLogs文件夹不存在,就会抛出DirectoryNotFoundException。你得先用System.IO.Directory类的方法(比如Directory.CreateDirectory("myfolder"))确保文件夹都存在。

怎么查文件的创建日期、修改日期或大小?
要获取文件的详细信息,比如创建时间、最后修改时间、大小、属性(隐藏、只读等),用System.IO.FileInfo类。它有很多有用的属性。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION