根据之前的文章可以知道,stream的世界里全是二进制。二进制数据可以在网络中传输,可以存储在计算机中。在冰冷的计算机的世界里,它们只能处理二进制数据。
但是作为人类而言,是没有办法阅读和理解二进制数据的。人类所能理解是人类的文字,比如中国人可以理解中文;美国人可以理解英文;日本人可以理解日文。
人类文字的最小单位是字符。比如:
中文的:你,我,他 。。。
英文的:a,b,c...
它们都是一个一个的字符,通过把这些字符连接起来,我们就可能创作出优美的诗词和文章。
所以计算机和人类之间有一道鸿沟,我们需要在二进制和人类文字之间建立一座桥梁,它就是Encoding技术。
因为最早计算机是美国人发明的,所以当时推出来的ASCII字符集只支持英文。后来计算机在全球范围迅速发展,ASCII字符集不能支持其他国家的语言,最后推出来了UTF8字符集,它支持所有的人类语言。
我们使用UTF8字符集对字符串和二进制进行互相转换:
//string to byte[]
Encoding.UTF8.GetBytes(str);
//byte[] to string
Encoding.UTF8.GetString(bytes);
在.Net Framework中,Encoding定义了很多种转换方法,我根据源码列出几个典型方法:
byte[] GetBytes(char[] chars);
byte[] GetBytes(string s);
char[] GetChars(byte[] bytes);
string GetString(byte[] bytes);
我们可以通过这几个方法,就可以在二进制和人类文字之间自由穿梭。
在C#中,char[]和string之间的转换如下:
//string to char[]
string ss = "abcdefg";
char[] cc = ss.ToCharArray();
//char[] to string
string str = new string(cc);
好,文章到这里已经完成了必要的铺垫,下面请文章的绝对主角粉墨登场:StreamReader类。
public class StreamReader : TextReader { ... }
StreamReader类扩展了TextReader抽象类,TextReader是StreamReader的父类。TextReader类的背后是char[],TextReader类封装了char[], 并提供了一些方法,用于处理内部的char[]。
在TextReader类中的方法:
//读取当前char的下一个char,当返回值是-1时,表示下个char已经是最后一个位置的char了
int Peek();
//读取当前char的下一个char,但是和peek方法不同,Read()方法使指针指向下个字符,但是Peek还是指向原来那个字符
int Read();
//读取TextReader内部的char[]数据到buffer中,index是char[] buffer的起始位置,count是保存的数量
int Read(char[] buffer, int index, int count);
//读取指针行的数据并返回字符串,指针移到下一行,返回null,表示数据全部读取完了
string ReadLine();
//指针当前位置到结尾的所有字符的字符串
string ReadToEnd();
//用完之后应该主动关闭
void Close();
void Dispose();
因为TextReader是抽象类,所以我们使用StringReader来对TextReader进行简单的练习:
string text = "abc\nabc";
using (TextReader reader = new StringReader(text))
{
while (reader.Peek() != -1)
{
Console.WriteLine("Read = {0}", (char)reader.Read());
}
}
using (TextReader reader = new StringReader(text))
{
char[] buffer = new char[3];
int data = reader.Read(buffer, 0, 3);
for (int i = 0; i < buffer.Length; i++)
{
Console.WriteLine("通过Read读出的数据:{0}", buffer[i]);
}
}
using (TextReader reader = new StringReader(text))
{
string lineData = reader.ReadLine();
Console.WriteLine("第一行的数据为:{0}", lineData);
}
using (TextReader reader = new StringReader(text))
{
string allData = reader.ReadToEnd();
Console.WriteLine("全部的数据为:{0}", allData);
}
好,文章到这里我们学习了StreamReader的父类TextReader,那StreamReader在TextReader的基础上,它又提供了哪些能力呢?
其实StreamReader类存在的意义是封装了从流到字符串的转换。封装了从二进制到人类文字的升华。我们看一下它的构造函数:
StreamReader(Stream stream);
StreamReader(Stream stream, Encoding encoding);
StreamReader(string path);
StreamReader(string path, Encoding encoding);
通过上面的构造函数,我们总结一下:
1. 可以传入一个Stream对象,完成StreamReader的初始化。
这种方式很巧妙,在之前的文章里面,我们知道.Net Framework中已经定义了很多种的Stream,例如MemoryStream,FileStream,BufferedStream等等,但是StreamReader提供了一个统一的方式来处理流,并不关心流的对象到底是MemoryStream还是FileStream类型的。
2. 可以选择传入一个Encoding对象,不传的话,默认是Encoding.UTF8。
3. 可以传入一个文件路径,这样文件内容,就会以流的形式加载到内存中。
属性:
//获取当前的stream对象
Stream BaseStream;
//获取当前StreamReader的Encoding
Encoding CurrentEncoding;
//判断StreamReader是否已经处于当前流的末尾
bool EndOfStream;
最后我们练习一下StreamReader类:
string txtFilePath="D:\\TextReader.txt";
//利用FileStream类将文件文本数据变成流然后放入StreamReader构造函数中
using(FileStream stream = File.OpenRead(txtFilePath))
{
using (StreamReader reader = new StreamReader(stream))
{
DisplayResultStringByUsingRead(reader);
}
}
using (FileStream stream = File.OpenRead(txtFilePath))
{
using (StreamReader reader = new StreamReader(stream, false))
{
DisplayResultStringByUsingRead(reader);
}
}
//也可以通过File.OpenText方法直接获取到StreamReader对象
using (StreamReader reader = File.OpenText(txtFilePath))
{
DisplayResultStringByUsingRead(reader);
}
public static void DisplayResultStringByUsingRead(StreamReader reader)
{
int readChar = 0;
string result = string.Empty;
while ((readChar=reader.Read()) != -1)
{
result += (char)readChar;
}
Console.WriteLine("使用StreamReader.Read()方法得到Text文件中的数据为 : {0}", result);
}
文章到了这里,已经接近尾声,加上一句比较有哲理的句子,为这篇文章画上一个句号:
一个人,只有当他的灵魂真正撼动了、刺痛了,他的智慧才可能被空前的发掘。人一旦变得清醒、理智起来,他就一步步靠近了成熟。