为什么要写这篇文章呢?因为愚钝的我,老是记不住各种语言的文件操作,每当切换到一个新的语言,就要重新百度一次文件操作,而且还各种坑。
C#
System.IO.File 和 System.IO.Directory
C# 在System.IO.File
和System.IO.Directory
这两个静态类中提供了大量的有关目录和文件操作的静态方法,在绝大部分情况下足够用了。写文本文件时,比如
public static void WriteAllText(string path, string contents);
会自动打开一个文件,写入内容后,会自动帮你关闭文件。无需为文件的打开没有关闭而烦恼。
FileStream, StreamReader 和 StreamWriter
在文件读写方面,C#还提供了提供了另外三种重要的类——FileStream
,StreamReader
,StreamWriter
,都是在System.IO
命名空间中。
FileStream
继承Stream
,是一个流,并且是字节流。只能处理字节数据。如果你查看他的所有成员方法,你会发现读写都是以byte
为单位。
StreamWriter
继承TextWriter
,StreamReader
继承TextReader
,是一个流装饰器(Stream-Decorator)。这两个类的读写方法允许接受诸如string
,char
这样的类型,但是它其实只是帮你把它们转化成字节流,然后从关联的流里进行读写。也就是说,你在使用一个流装饰器的时候,一定也在使用一个流。这个流被称作这个流装饰器的基础流(BaseStream)。你可以通过以下代码获取到一个流装饰器的基础流。
// StreamWriter sw;
Stream s = sw.BaseStream();
一般来说,创建一个流装饰器的方法可以如下:
// 先创建一个流
FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
此时,写完文件之后,你需要先关闭流装饰器,然后再关闭关联的流。或者如果流装饰器关联的流被关闭,那么这个流装饰器会自动关闭。你可以这样用流装饰器里写一个字符串:
sw.WriteLine("Hello World");
但是,具体这个字符串的写入方式只跟关联的流有关。如果你创建流的时候,FileMode指定的是Append,那么这个字符串会添加到文件的末尾。如果这个FileMode只是OpenOrCreate,那么这个字符串会从开头开始写,覆盖原本在文件开头的内容。
另外,写完之后,一定要在程序关闭之前关闭流装饰器。否则字符串可能并没有真正被写入到文件中。因为流装饰器会先把内容写入到自己的缓冲区中,只有在缓冲区满了,或者流装饰器被关闭了,缓冲区的内容才会被写入文件里。可以使用如下代码强制将缓冲区内容写入文件中:
sw.Flush(); // if no sw.Close();
当然,你如果不用流装饰器来写,而是直接使用流写,那么流也会有自己的缓冲区。除非你记得关闭流,或者使用fileStream.Flush()
,否则流缓冲区里的内容是不会自动写入文件里的。下面是一个用流直接写文件的例子。
// using System.Text;
byte[] array = Encoding.UTF8.GetBytes("最新的一行");
fs.Write(array, 0, array.Length);
fs.Flush(); // if no fs.Close();
如果你一般只是写文本文件,且习惯用流装饰器去写,你也可以直接使用System.IO.File
提供的如下几个静态方法去创建流装饰器,它会自动帮你创建并关联到一个指定的流,同时不需要你去关心流的关闭。
// 相当于 FileMode.Append, FileAccess.Write
public static StreamWriter AppendText(string path);
// 相当于 FileMode.Append.OpenOrCreate,FileAccess.Write
public static StreamWriter CreateText(string path);
// 相当于 FileMode.Open, FileAccess.Read
public static StreamReader OpenText(string path);
Java
请看这篇文章