使用Reader和Writer的文本I/O
TextWriter
类和TextReader
类是基于文本的抽象类,它们的重要子类包括:StreamWriter
、StreamReader
,处理流的操作;StringWriter
、StringReader
,处理字符串的操作。注意:对于C程序员来说,StreamWriter
类似于printf()
或者fprintf()
,而StringWriter
类似于sprintf()
。
1.TextWriter
TextWriter
是一个抽象基类,它包含下面一些子类:
- ① 用来为浏览器客户端编写HTML的
HtmlTextWriter
- ② 用来向ASP.NET网页中的HTTP响应对象写入文本的
HttpWriter
- ③ 使用缩进控制写入文本的
IndentedTextWriter
- ④ 向流中写入字符的
StringWriter
- ⑤ 向字符串中写入字符的
StringWriter
TextWriter
有3个属性:Encoding
,用来返回产生输出的字符编码;FormatProvider
,用来引用对文本进行格式化的对象;NewLine
,用来返回当前使用平台上所用的行结束符。
TextWriter类中的方法:
Close
,关闭TextWriter并释放所有与之相关的资源Dispose
,释放与TextWriter相关的资源Flush
,将保留在TextWriter缓冲区中的未录入数据写入Synchronized
,创建TextWriter对象的一个线程安全包Write
,向流中写入数据WriteLine
,向流中写入数据并换行
TextWriter的Write()
方法是将数据以字符串的方式写出。要注意与其他类相比:Stream
的Write()
方法写的是字节,BinaryWriter
的Writer()
方法是将基本数据类型以原始的方式写出。
Write()
方法可以将object
对象写出,它会调用对象的ToString()
方法转成字符串。还可以带格式字符串,格式字符串的用法与Console.Write()
方法相似,事实上,Console.Write()
方法调用的就是TextWriter
的方法。
静态的Synchronized()
方法可以为TextWriter
创建一个线程安全包装来保证其安全性,使得用到同一个TextWriter
的两个线程彼此之间不会相互干扰。
2.StreamWriter
StreamWriter是TextWriter的一个子类,用来通过特定的编码方式向流中写入字符。默认的编码方式是UTF-8
,该方式适用于操作系统本地版本上的统一编码标准的字符。如果想使用其他的编码方式,可以使用System.Text
命名空间所提供的ASCII和UTF-7编码,或者根据System.Text.Encoding
创建自己的编码方式。
当构造一个StreamWriter对象时,可以指定一个文件名或者现存的流的名称,并指定一种编码方式。下面的代码片段展示了如何创建一个向文件中写入内容的StreamWriter对象:
FileStream fs = new FileStream(@"C:\temp\foo.txt", FileMode.Create);
StreamWriter writer = new StreamWriter(fs);
其中,FileStream对象用来向foo.txt文件中写入内容,StreamWriter对象的作用是把字符转换成字节然后输出到FileStream
中去。
StreamWriter
有一个AutoFlush
属性,该属性为真时,对象美执行一次Write()
操作都会刷新缓冲区。这样可以确保输出内容一直是最新的,但这种做法不如允许StreamWriter
缓冲其输出内容那样有效。BaseStream
属性提供对基本Stream对象的访问。
除了从TextWriter
中继承的方法之外,StreamWriter
类没有添加任何新方法,但重载了用于向流中写入字符和字符串的Write()
方法。
3.StringWriter方法
StringWriter
用来将其输出内容写入到一个字符串中。由于被写入的字符串处于被修改状态,因此输出内容实际上是被写入StringBuilder
,而不是被写入到String
中,因为String
是不可以被改变的。
StringWriter拥有一套Write()
方法,以及GetStringBuiler()
和ToString()
方法,用来处理字符串创建中的缓冲器。
下面代码展示了如何创建和使用StringWriter
:
StringWriter sw = new StringWriter();
int n = 42;
sw.Write("The value of n is {0}", n);
sw.Write("... and some more characters");
Console.WriteLine(sw.ToString());
4.TextReader
TextReader
类用于读取字符串。TextReader
类有两个子类StreamReader
和StringReader
。该类中包含的方法比TextWriter
类要少,但其处理方式是一样的:
Close
,关闭TextReader并释放所有与之相关的资源Peek
,浏览下一字符,但不将其从输入流中移走Read
,将字符读入到字符数组中ReadBlock
,将字符持续读入到字符数组或者块中,直到读入了足够的字符数或者到达了文件尾ReadLine
,读入一行字符,并将其作为字符串返回ReadToEnd
,一直读入到流的尾部,并将所有的字符作为一个字符串返回Synchronized
,创建一个TextReader的一个线程安全包
5.StreamReader
StreamReader
类支持流中的面向字符的输入,因此该类可以用来从文件中逐行读取文本。StreamReader
可以使用任何一种选定的字符编码方式,但在默认情况下使用的是UTF-8方式,因为这种编码方式可以处理统一编码标准下的字符。
在创建StreamReader
时,根据下面的不同情况的参数,共有10中可选的构造器以不同的方式来创建对象:
- ① 根据文件名,也可以不指定字符编码方式
- ② 根据
Stream
引用,同样也可以不指定编码方式
StreamReader类共有两个属性:BaseStream
,用来返回对该类中所包含的Stream的引用;CurrentEncoding
,用来返回当前reader对象所使用的的字符编码方式。
StreamReader
中包含有好几种不同的读取数据的方式。ReadLine()
方法用来读取某一行数据,并将其作为字符串返回。ReadToEnd()
方法用来读取整个流,也是以一个字符串的形式返回。同时还有两种Read()
方法,其中一种用来返回流中的下一个字符(如果已经到达了流的末尾,则返回-1
),另一种用来将指定数量的字符读入到下一个字符数组中。此外,可以通过Peek()
方法浏览流中的下一个字符而不将其中流中移走。
6.StringReader
使用StringReader可以从字符串中读取字符,每次可以读取一个、多个或者是一整行字符。如果需要把字符串当作文本文件来处理,这个类是很有用的。
7.应用举例
读入每一个C#文件,将每行中的注释去掉,并加上行号,写入另一文件:
using System;
using System.IO;
using System.Text;
namespace ConsoleApp18流_文件IO
{
class CopyFileAddLineNumber
{
public static void MMain(string[] args) {
string infname = @"E:\Visual Studio 2019\C#\Mooc学习\ConsoleApp18流、文件IO\CopyFileAddLineNumber.cs";
string outfname = "CopyFileAddLineNumber.txt";
if (args.Length >= 1) infname = args[0];
if (args.Length >= 2) outfname = args[1];
try {
FileStream fin = new FileStream(infname, FileMode.Open, FileAccess.Read);
FileStream fout = new FileStream(outfname, FileMode.Create, FileAccess.Write);
StreamReader brin = new StreamReader(fin, Encoding.Default);
StreamWriter brout = new StreamWriter(fout, Encoding.Default);
int cnt = 0; // 行号
string s = brin.ReadLine();
while (s != null) {
cnt++;
s = deleteComments(s); // 去掉以 // 开始的注释
brout.WriteLine(cnt + ":\t" + s); // 写入
Console.WriteLine(cnt + ":\t" + s); // 在控制上显示
s = brin.ReadLine(); // 读入
}
brin.Close(); // 关闭缓冲读入流及文件读入流的连接
brout.Close();
} catch (FileNotFoundException) {
Console.WriteLine("File not found!");
} catch (Exception e2) {
Console.WriteLine(e2);
}
}
private static string deleteComments(string s) {
// 去掉以 // 开始的注释
if (s == null) return s;
int pos = s.IndexOf("//");
if (pos < 0) return s;
return s.Substring(0, pos);
}
}
}
运行结果:
使用File的文本文件功能
文本文件的处理是比较常用的,所以.NET Framework
提供了专门的File类来处理文本文件的相关功能,File是个static类,直接使用“File.方法
”即可。
这些方法中,一类是得到一个流以方便操作,如File.OpenText(path)
或File.AppendText(path)
得到一个UTF-8编码的StreamWriter
,而File.OpenText(path)
得到一个UTF-8编码的StreamReader
。
另一类则更方便,打开、读写、关闭文件只有一个方法就行了,包括:
File.ReadAllText(path, encoding)
读到一个文件的所有文本File.ReadAllLines(path, encoding)
读到一个文件的所有行的数组File.WriteAllText(path, text, encoding)
将文本写入到一个文件中File.WriteAllLines(path, lines, encoding)
将文本数组写入到一个文件中File.AppendAllText(path, text, encoding)
将文本附加到一个文件中FIle.AppendAllLines(path, lines, encoding)
将文本数组附加到一个文件中
另外,还有一个File.ReadAllBytes(path)可以一次性地读入任意文件所有字节内容。
例,FileReadWrieText()使用File类来读写文件:
public static void FileReadwriteText() {
string path = @"E:\Visual Studio 2019\C#\Mooc学习\ConsoleApp18流、文件IO\bin\Debug\FileText.txt";
// 创建文件(UTF-8编码)
if (!File.Exists(path)) {
using (StreamWriter sw = File.CreateText(path)) {
sw.WriteLine("Hello");
sw.WriteLine("And");
sw.WriteLine("Welcome");
}
}
// 读文件(UTF-8编码)
using (StreamReader sr = File.OpenText(path)) {
string s = "";
while ((s = sr.ReadLine()) != null) {
Console.WriteLine(s);
}
}
}
例,FileReadWriteAlLines使用File类来一次性读写文本文件。
public static void FileReadwriteAllLines() {
string path = @"E:\Visual Studio 2019\C#\Mooc学习\ConsoleApp18流、文件IO\bin\Debug\FileText1.txt";
Encoding encoding = Encoding.Default;
// 一次性写入文件内容
File.WriteAllText(path, "hello\nworld\n", encoding);
// 一次性追加文件内容
File.AppendAllLines(path, new string[] {
"good", "file" }, encoding);
// 一次性读文件内容
string[] lines = File.ReadAllLines(path, encoding);
foreach (string s in lines)
Console.WriteLine(s);
}
标准输入/输出
计算机系统都有默认的标椎输入设备和输出设备。对一般的系统,标准输入通常是键盘,标准输出通常是显示器屏幕。C#程序使用字符界面与系统标准输入/输出间进行数据通信,即从键盘读入数据,或向屏幕输出数据,是十分常见的操作。
在C#中可以用Console
来处理控制台的操作。Console有三个static
的属性:Console.In
、Console.Out
、Console.Error
,分别于系统的标准输入、标准输出及标准错误输出相联系。
Console.In
是TextReader
类的对象,Console.Out
及Console.Error
是TextWriter
类的对象。可以用它们来进行各种各样的读写操作。
事实上,Console.Read()
,Console.ReadLine()
,Console.Write()
,Console.WriteLine()
这几个方法会将相应的操作转向到Console.In
及Console.Out
。