文件的输入与输出
一个文件是一个存储在磁盘中带有指定名称和目录路径的数据集合。当打开文件进行读写时,它变成一个流。
从根本上说,流是通过通信路径传递的字节序列。有两个主要的流:输入流 和 输出流。输入流用于从文件读取数据(读操作),输出流用于向文件写入数据(写操作)。
I/O 类
System.IO 命名空间有各种不同的类,用于执行各种文件操作,如创建和删除文件、读取或写入文件,关闭文件等。
下表列出了一些 System.IO 命名空间中常用的非抽象类:
I/O 类 | 描述 |
---|---|
BinaryReader | 从二进制流读取原始数据。 |
BinaryWriter | 以二进制格式写入原始数据。 |
BufferedStream | 字节流的临时存储。 |
Directory | 有助于操作目录结构。 |
DirectoryInfo | 用于对目录执行操作。 |
DriveInfo | 提供驱动器的信息。 |
File | 有助于处理文件。 |
FileInfo | 用于对文件执行操作。 |
FileStream | 用于文件中任何位置的读写。 |
MemoryStream | 用于随机访问存储在内存中的数据流。 |
Path | 对路径信息执行操作。 |
StreamReader | 用于从字节流中读取字符。 |
StreamWriter | 用于向一个流中写入字符。 |
StringReader | 用于读取字符串缓冲区。 |
StringWriter | 用于写入字符串缓冲区。 |
FileStream 类
System.IO 命名空间中的 FileStream 类有助于文件的读写与关闭。该类派生自抽象类 Stream。
您需要创建一个 FileStream 对象来创建一个新的文件,或打开一个已有的文件。创建 FileStream 对象的语法如下:
FileStream <object_name> = new FileStream( <file_name>,<FileMode Enumerator>, <FileAccess Enumerator>, <FileShare Enumerator>);
例如,创建一个 FileStream 对象 F 来读取名为 sample.txt 的文件:
FileStream F = new FileStream("sample.txt", FileMode.Open, FileAccess.Read, FileShare.Read);
参数:
- FileMode 枚举定义了各种打开文件的方法。FileMode 枚举的成员有:
- Append:打开一个已有的文件,并将光标放置在文件的末尾。如果文件不存在,则创建文件。
- Create:创建一个新的文件。如果文件已存在,则删除旧文件,然后创建新文件。
- CreateNew:指定操作系统应创建一个新的文件。如果文件已存在,则抛出异常。
- Open:打开一个已有的文件。如果文件不存在,则抛出异常。
- OpenOrCreate:指定操作系统应打开一个已有的文件。如果文件不存在,则用指定的名称创建一个新的文件打开。
- Truncate:打开一个已有的文件,文件一旦打开,就将被截断为零字节大小。然后我们可以向文件写入全新的数据,但是保留文件的初始创建日期。如果文件不存在,则抛出异常。
- FileAccess:FileAccess 枚举的成员有:Read、ReadWrite 和 Write。
- FileShare:FileShare 枚举的成员有:
- Inheritable:允许文件句柄可由子进程继承。Win32 不直接支持此功能。
- None:谢绝共享当前文件。文件关闭前,打开该文件的任何请求(由此进程或另一进程发出的请求)都将失败。
- Read:允许随后打开文件读取。如果未指定此标志,则文件关闭前,任何打开该文件以进行读取的请求(由此进程或另一进程发出的请求)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。
- ReadWrite:允许随后打开文件读取或写入。如果未指定此标志,则文件关闭前,任何打开该文件以进行读取或写入的请求(由此进程或另一进程发出)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。
- Write:允许随后打开文件写入。如果未指定此标志,则文件关闭前,任何打开该文件以进行写入的请求(由此进程或另一进过程发出的请求)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。
- Delete:允许随后删除文件。
实例
下面的程序演示了 FileStream 类的用法:
// 注意导入 using System.IO; 命名空间
// 写入的文件去 项目名/项目名/bin/Debug 文件中去查找
FileStream file = new FileStream("test.json", FileMode.OpenOrCreate, FileAccess.ReadWrite);
for (byte i = 0; i < 10; i++)
{// file.Write(new byte[] {1,2,3},0,3);file.WriteByte((byte)i);
}
// 获取或设置此流的当前位置。
file.Position = 0;
for (int i = 0; i < 10; i++)
{// 若不含字符值为 -1Console.WriteLine(file.ReadByte() + " ");
}
file.Close();
实例 BinaryWriter 和 BinaryReader
下面的程序演示了 BinaryWriter 和 BinaryReader 类的用法:
从二进制流读取原始数据 和 以二进制格式写入原始数据
FileStream fileStream = new FileStream(@"test.json", FileMode.Create, FileAccess.Write);
// 创建二进制写入流的实例
BinaryWriter binaryWriter = new BinaryWriter(fileStream);
// 向文件中写入图书名称
binaryWriter.Write("C#基础教程");
binaryWriter.Write("abc");
// 向文件中写入图书价格
binaryWriter.Write(49.5);
binaryWriter.Write(true);
// 清除缓冲区的内容,将缓冲区中的内容写入到文件中
binaryWriter.Flush();
// 关闭二进制流
binaryWriter.Close();
// 关闭文件流
fileStream.Close();fileStream = new FileStream(@"test.json", FileMode.Open, FileAccess.Read);
// 创建二进制读取流的实例
BinaryReader binaryReader = new BinaryReader(fileStream);
// 输出图书名称
Console.WriteLine(binaryReader.ReadString());
Console.WriteLine(binaryReader.ReadString());
// 输出图书价格
Console.WriteLine(binaryReader.ReadDouble());
Console.WriteLine(binaryReader.ReadBoolean());
// 关闭二进制读取流
binaryReader.Close();
// 关闭文件流
fileStream.Close();/*
FileStream fileStream = new FileStream(@"test.json", FileMode.Create, FileAccess.Write);
// 创建二进制写入流的实例
BinaryWriter binaryWriter = new BinaryWriter(fileStream);
// 向文件中写入图书名称
binaryWriter.Write(new UTF8Encoding().GetBytes("C#基础教程"));
binaryWriter.Write(new UTF8Encoding().GetBytes("abc"));
// 向文件中写入图书价格
binaryWriter.Write(new UTF8Encoding().GetBytes("49.5"));
binaryWriter.Write(new UTF8Encoding().GetBytes("true"));
// 清除缓冲区的内容,将缓冲区中的内容写入到文件中
binaryWriter.Flush();
// 关闭二进制流
binaryWriter.Close();
// 关闭文件流
fileStream.Close();fileStream = new FileStream(@"test.json", FileMode.Open, FileAccess.Read);
// 创建二进制读取流的实例
BinaryReader binaryReader = new BinaryReader(fileStream);
// 输出信息
byte[] data = new byte[fileStream.Length];
while(binaryReader.Read(data, 0, data.Length) > 0)Console.WriteLine(new UTF8Encoding().GetString(data));
// 关闭二进制读取流
binaryReader.Close();
// 关闭文件流
fileStream.Close();
*/
实例 BufferedStream
下面的程序演示了 BufferedStream 类的用法:
字节流的临时存储
// 缓冲区是内存中的字节块,用于缓存数据,从而减少对操作系统的调用失败次数。缓冲区可提高读取和写入性能。使用缓冲区可进行读取或写入操作,但不能同时进行这两种操作。BufferedStream类用于读写缓冲区。// File:有助于处理文件
// 如果存在该文件
if (File.Exists("test.json")) {// 删除该文件File.Delete("test.json");
}// 创建BufferedStream对象的语法如下:
// BufferedStream 对象名 = new BufferedStream(Stream stname);
// BufferedStream 对象名 = new BufferedStream(Stream stname, int size);
// 这两种方法都可以创建BufferedStream流对象,前者只有一个参数——Stream实例,后者在此基础上增加了表示缓冲区大小的整型数据。默认情况下,缓冲区的大小是4096字节。
// BufferedStream对象是包裹已经创建的现有流对象而形成的,要使用BufferedStream,需要用BufferedStream,需要先创建一个Stream流对象。创建方法如下:
// Stream instream = File.OpenRead(文件名);
// stream outstream = File.OpenWrite(文件名);
BufferedStream bs = new BufferedStream(File.Create("test.json"));// 创建了BufferedStream对象后,可以用该对象调用Read()和Write()方法,实现数据的读写。示例代码如下:
// 借助于 new UTF8Encoding() 实现编码,可以有效防止乱码
byte[] info1 = new UTF8Encoding().GetBytes("The first line ");
bs.Write(info1, 0, info1.Length);
byte[] info2 = new UTF8Encoding().GetBytes("123456789\r\n");
bs.Write(info2, 0, info2.Length);
byte[] info3 = new UTF8Encoding().GetBytes("The second line\r\n");
bs.Write(info3, 0, info3.Length);
byte[] info4 = new UTF8Encoding().GetBytes("Another line");
bs.Write(info4, 0, info4.Length);// 最后一定要清空缓冲区,以确保数据全部写入文件。
bs.Flush();
bs.Close();// BufferedStream.Write 方法:将字节复制到缓冲流,并将缓冲流内的当前位置前进写入的字节数。
// BufferedStream.Read 方法:将字节从当前缓冲流复制到数组。
// BufferedStream.Seek 方法:设置当前缓冲流中的位置。bs = new BufferedStream(File.OpenRead("test.json"));
byte[] b = new byte[bs.Length];
UTF8Encoding utf8 = new UTF8Encoding();
// 若不含字符值为 -1
while (bs.Read(b, 0, b.Length) > 0)Console.WriteLine(utf8.GetString(b));
实例 Directory 类
下面的程序演示了 Directory 类的操作用法(路径建议用@字符串,防止\转义):
// Directory类位于System.IO 命名空间。Directory类提供了在目录和子目录中进行创建移动和列举操作的静态方法。此外,你还可以访问和操作各种各样的目录属性,例如创建或最后一次修改时间以及Windows访问控制列表等。// System.IO.Directory类和System.DirectoryInfo类
// 主要提供关于目录的各种操作,使用时需要引用System.IO命名空间。下面通过程序实例来介绍其主要属性和方法。// 1.目录创建方法:Directory.CreateDirectory(打开目录去验证)
// 下面的代码演示在 工程的Debug 文件夹下创建名为NewDirectory的目录。
Directory.CreateDirectory(@"C:\Users\机械革命\Desktop\C#\test\test\bin\Debug\NewDirection");
Directory.CreateDirectory(@"NewDirection");// 2.目录属性设置方法:DirectoryInfo.Atttributes
// 下面的代码设置工程的Debug文件夹下名为NewDirectory的目录为只读、隐藏。与文件属性相同,目录属性也是使用FileAttributes来进行设置的。
DirectoryInfo NewDirInfo = new DirectoryInfo(@"NewDirection");
NewDirInfo.Attributes = FileAttributes.ReadOnly | FileAttributes.Hidden;// 3.目录删除方法:Directory.Delete
// 下面的代码可以将 NewDirection 目录删除。Delete方法的第二个参数为bool类型,它可以决定是否删除非空目录。如果该参数值为true,将删除整个目录,即使该目录下有文件或 子目录;若为false,则仅当目录为空时才可删除。
Directory.Delete(@"NewDirection", true);// 4.目录移动方法:Directory.Move
// 下面的代码将目录 NewDirection 移动到 Release 文件夹下
Directory.Move(@"NewDirection", @"../Release/NewDirection");// 5.获取当前目录下的所有子目录方法:Directory.GetDirectories
// 下面的代码读出 Debug 目录下的所有子目录(手动创建一个文件夹,否则为0),并将其存储到字符串数组中。
string[] Directorys = Directory.GetDirectories(@"..\Debug");
foreach (string dir in Directorys)
{Console.WriteLine(dir);
}// 6.获取当前目录下的所有文件方法:Directory.GetFiles
// 下面的代码读出 Debug 目录下的所有文件,并将其存储到字符串数组中。
string[] Files = Directory.GetFiles(@"..\Debug");
foreach (string File in Files)
{Console.WriteLine(File);
}// 7.判断目录是否存在方法:Directory.Exist
Console.WriteLine(Directory.Exists(@"NewDirection"));
实例 File 类
下面的程序演示了 File 类的操作用法(路径建议用@字符串,防止\转义):
// 1.File.Create(string path):在指定路径中创建或覆盖一个文件(注意:如果指定路径下已经有此文件,那么原有的会被覆盖)
// 返回值为 FileStream
FileStream file = File.Create(@"test.json");// 2.File.Delete(string path):根据指定路径删除一个文件(注意:此删除是彻底删除,回收站也没有),如果上面代码没有注释的话,加上:file.Close();,否则不能被删除
File.Delete(@"test.json");// 3.File.Copy(string originPath, string newPath):根据指定路径复制一个文件;
// 参数一:要复制的文件路径
// 参数二:复制出来的新的文件路径及文件名字
// 注意:
// 1> 如果要复制的文件不存在,编辑器会报错;
// 2> 如果参数一和参数二路径及文件名一样,编辑器会报错;
// 3> 新复制出来的文件与原来文件大小、里边内容都是一样的。
File.Copy(@"C:\Users\Administrator\Desktop\debug.log", @"C:\Users\Administrator\Desktop\newDebug.log");// 4.File.Exists(string originPath):判断一个文件是否存在,参数为文件路径,返回值为布尔类型;
bool isExists = File.Exists(@"C:\Users\Administrator\Desktop\newDebug.log");
Console.WriteLine(isExists);// 5.File.Move():将指定的文件移到新位置(等于剪切一个文件到新的地方);
// 参数一:原始文件路径;
// 参数二:新的文件路径及名字。
// 注意:
// 1> 如果新指定的文件路径和文件名存在则编辑器会报错;
File.Move(@"C:\Users\Administrator\Desktop\debug.log", @"C:\Users\Administrator\Desktop\newDebug.log");// 6.File.ReadAllBytes():读取一个文件内容,返回的是这个文件内容的字节数组;
// 写入文件
File.WriteAllBytes(@"1.txt",new UTF8Encoding().GetBytes("你好,C#!"));
// 以二进制字节数组的形式给读出来
byte[] buffer = File.ReadAllBytes(@"1.txt");
// 使用下边方法把二进制字节数组转为字符串,我们才能看到真正的内容
// 下边Encoding后边是一个编码格式,默认使用UTF8即可
string str = new UTF8Encoding().GetString(buffer);
Console.WriteLine(str);// 7.File.ReadAllLines(string path, Encoding.encoding):读取一个文件的所有行,返回包含文件所有行的字符串数组;
// 参数一:文件的路径;
// 参数二:编码格式,如果不指定编码格式,所输出的文件内容很有可能是乱码!!!
// 写入文件
string[] infos = new string[] { @"你好,C#!\n\r", @"你好,C#!\n\r", @"你好,C#!\n\r", @"你好,C#!\n\r", @"你好,C#!\n\r" };
File.WriteAllLines(@"1.txt", infos, new UTF8Encoding());
// 读取文件
string[] str = File.ReadAllLines(@"1.txt", Encoding.UTF8);
Console.WriteLine(str.Length);
for (int i = 0; i < str.Length; i++)
{Console.WriteLine(str[i]);
}// 8.File.ReadAllText(string path, Encoding.encoding):读取一个文件的所有行,返回包含文件所有行的字符串;
// 与ReadAllLines区别在于:返回的结果不同;
string infos = File.ReadAllText(@"1.txt", Encoding.UTF8);
Console.WriteLine(infos);
// File.WriteAllText(@"1.txt","hahahahah");
// string infos = File.ReadAllText(@"1.txt", Encoding.UTF8);
// Console.WriteLine(infos);
C# 高级文件操作
上面的实例演示了 C# 中简单的文件操作。但是,要充分利用 C# System.IO 类的强大功能,您需要知道这些类常用的属性和方法。
文本文件的读写
它涉及到文本文件的读写。StreamReader 和 StreamWriter 类有助于完成文本文件的读写。
StreamReader 类
StreamReader 类继承自抽象基类 TextReader,表示阅读器读取一系列字符。
下表列出了 StreamReader 类中一些常用的方法:
序号 | 方法 | 描述 |
---|---|---|
1 | public override void Close() | 关闭 StreamReader 对象和基础流,并释放任何与读者相关的系统资源。 |
2 | public override int Peek() | 返回下一个可用的字符,但不使用它。 |
3 | public override int Read() | 从输入流中读取下一个字符,并把字符位置往前移一个字符。 |
实例
下面的实例演示了读取名为 Jamaica.txt 的文件。文件如下:
Down the way where the nights are gay
And the sun shines daily on the mountain top
I took a trip on a sailing ship
And when I reached Jamaica
I made a stop 中文
try
{// 创建一个 StreamReader 的实例来读取文件 // using 语句也能关闭 StreamReaderusing (StreamReader sr = new StreamReader(@"jamaica.txt", Encoding.UTF8)){string line;// 从文件读取并显示行,直到文件的末尾 while ((line = sr.ReadLine()) != null){Console.WriteLine(line);}}
}
catch (Exception e)
{// 向用户显示出错消息Console.WriteLine("The file could not be read:");Console.WriteLine(e.Message);
}
StreamWriter 类
StreamWriter 类继承自抽象类 TextWriter,表示编写器写入一系列字符。
下表列出了 StreamWriter 类中一些常用的方法:
序号 | 方法 | 描述 |
---|---|---|
1 | public override void Close() | 关闭当前的 StreamWriter 对象和基础流。 |
2 | public override void Flush() | 清理当前编写器的所有缓冲区,使得所有缓冲数据写入基础流。 |
3 | public virtual void Write(bool value) | 把一个布尔值的文本表示形式写入到文本字符串或流。(继承自 TextWriter。) |
4 | public override void Write(char value) | 把一个字符写入到流。 |
5 | public virtual void Write(decimal value) | 把一个十进制值的文本表示形式写入到文本字符串或流。 |
6 | public virtual void Write(double value) | 把一个 8 字节浮点值的文本表示形式写入到文本字符串或流。 |
7 | public virtual void Write(int value) | 把一个 4 字节有符号整数的文本表示形式写入到文本字符串或流。 |
8 | public override void Write(string value) | 把一个字符串写入到流。 |
9 | public virtual void WriteLine() | 把行结束符写入到文本字符串或流。 |
实例
下面的实例演示了使用 StreamWriter 类向文件写入文本数据:
string[] names = new string[] { "Zara Ali", "Nuha Ali" };
using (StreamWriter sw = new StreamWriter("names.txt"))
{foreach (string s in names){sw.WriteLine(s);}
}// 从文件中读取并显示每行
string line = "";
using (StreamReader sr = new StreamReader("names.txt"))
{while ((line = sr.ReadLine()) != null){Console.WriteLine(line);}
}
中文乱码的情况
读取中文的时候会显示乱码,在读取文件内容的时候使用:
using (StreamReader sr = new StreamReader("C:/a.txt", Encoding.GetEncoding("GB2312")))
然后编译的时候会报错,无法编译。
报错的解决办法如下:
第一:
using System.Text;
第二:
在 .csproj 文件中应添加如下代码:
<ItemGroup> <PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" /> </ItemGroup
第三步:
在使用 System.Text.Encoding.GetEncoding (“GB2312”) 之前,在代码中执行:
System.Text.Encoding.RegisterProvider (System.Text.CodePagesEncodingProvider.Instance);
二进制文件的读写
它涉及到二进制文件的读写。BinaryReader 和 BinaryWriter 类有助于完成二进制文件的读写。
BinaryReader 和 BinaryWriter 类用于二进制文件的读写。
BinaryReader 类
BinaryReader 类用于从文件读取二进制数据。一个 BinaryReader 对象通过向它的构造函数传递 FileStream 对象而被创建。
下表列出了 BinaryReader 类中一些常用的方法:
序号 | 方法 | 描述 |
---|---|---|
1 | public override void Close() | 关闭 BinaryReader 对象和基础流。 |
2 | public virtual int Read() | 从基础流中读取字符,并把流的当前位置往前移。 |
3 | public virtual bool ReadBoolean() | 从当前流中读取一个布尔值,并把流的当前位置往前移一个字节。 |
4 | public virtual byte ReadByte() | 从当前流中读取下一个字节,并把流的当前位置往前移一个字节。 |
5 | public virtual byte[] ReadBytes( int count ) | 从当前流中读取指定数目的字节到一个字节数组中,并把流的当前位置往前移指定数目的字节。 |
6 | public virtual char ReadChar() | 从当前流中读取下一个字节,并把流的当前位置按照所使用的编码和从流中读取的指定的字符往前移。 |
7 | public virtual char[] ReadChars( int count ) | 从当前流中读取指定数目的字节,在一个字符数组中返回数组,并把流的当前位置按照所使用的编码和从流中读取的指定的字符往前移。 |
8 | public virtual double ReadDouble() | 从当前流中读取一个 8 字节浮点值,并把流的当前位置往前移八个字节。 |
9 | public virtual int ReadInt32() | 从当前流中读取一个 4 字节有符号整数,并把流的当前位置往前移四个字节。 |
10 | public virtual string ReadString() | 从当前流中读取一个字符串。字符串以长度作为前缀,同时编码为一个七位的整数。 |
BinaryWriter 类
BinaryWriter 类用于向文件写入二进制数据。一个 BinaryWriter 对象通过向它的构造函数传递 FileStream 对象而被创建。
下表列出了 BinaryWriter 类中一些常用的方法:
序号 | 方法 | 描述 |
---|---|---|
1 | public override void Close() | 关闭 BinaryWriter 对象和基础流。 |
2 | public virtual void Flush() | 清理当前编写器的所有缓冲区,使得所有缓冲数据写入基础设备。 |
3 | public virtual long Seek( int offset, SeekOrigin origin ) | 设置当前流内的位置。 |
4 | public virtual void Write( bool value ) | 把一个单字节的布尔值写入到当前流中,0 表示 false,1 表示 true。 |
5 | public virtual void Write( byte value ) | 把一个无符号字节写入到当前流中,并把流的位置往前移一个字节。 |
6 | public virtual void Write( byte[] buffer ) | 把一个字节数组写入到基础流中。 |
7 | public virtual void Write( char ch ) | 把一个 Unicode 字符写入到当前流中,并把流的当前位置按照所使用的编码和要写入到流中的指定的字符往前移。 |
8 | public virtual void Write( char[] chars ) | 把一个字符数组写入到当前流中,并把流的当前位置按照所使用的编码和要写入到流中的指定的字符往前移。 |
9 | public virtual void Write( double value ) | 把一个 8 字节浮点值写入到当前流中,并把流位置往前移八个字节。 |
10 | public virtual void Write( int value ) | 把一个 4 字节有符号整数写入到当前流中,并把流位置往前移四个字节。 |
11 | public virtual void Write( string value ) | 把一个以长度为前缀的字符串写入到 BinaryWriter 的当前编码的流中,并把流的当前位置按照所使用的编码和要写入到流中的指定的字符往前移。 |
实例
下面的实例演示了读取和写入二进制数据:
BinaryWriter bw;
BinaryReader br;
int i = 25;
double d = 3.14157;
bool b = true;
string s = "I am happy";
// 创建文件
try
{bw = new BinaryWriter(new FileStream("mydata.txt",FileMode.Create));
}
catch (IOException e)
{Console.WriteLine(e.Message + "\n Cannot create file.");return;
}
// 写入文件
try
{// 写入四行,每次写入一行bw.Write(i);bw.Write(d);bw.Write(b);bw.Write(s);
}
catch (IOException e)
{Console.WriteLine(e.Message + "\n Cannot write to file.");return;
}
// 执行完写入程序后关闭该二进制文件
bw.Close();
// 读取文件
try
{br = new BinaryReader(new FileStream("mydata.txt",FileMode.Open));
}
catch (IOException e)
{Console.WriteLine(e.Message + "\n Cannot open file.");return;
}
try
{// 按顺序读取数据,这里的读取方式对应了之前的存储方式,并且按顺序操作i = br.ReadInt32();Console.WriteLine("Integer data: {0}", i);d = br.ReadDouble();Console.WriteLine("Double data: {0}", d);b = br.ReadBoolean();Console.WriteLine("Boolean data: {0}", b);s = br.ReadString();Console.WriteLine("String data: {0}", s);
}
catch (IOException e)
{Console.WriteLine(e.Message + "\n Cannot read from file.");return;
}
// 读取完后关闭该二进制文件
br.Close();