zoukankan      html  css  js  c++  java
  • C#中流的读写器BinaryReader、BinaryWriter,StreamReader、StreamWriter详解

     
     
    次日补充内容:什么时候用BinaryReader(Writer),什么时候用StreamReader(Writer)
    C#的FileStream类提供了最原始的字节级上的文件读写功能,但我们习惯于对字符串操作,于是StreamWriter和 StreamReader类增强了FileStream,它让我们在字符串级别上操作文件,但有的时候我们还是需要在字节级上操作文件,却又不是一个字节 一个字节的操作,通常是2个、4个或8个字节这样操作,这便有了BinaryWriter和BinaryReader类,它们可以将一个字符或数字按指定 个数字节写入,也可以一次读取指定个数字节转为字符或数字。
     
    StreamWriter sr=new StreamWriter(@"xxxx.txt");  ->文本读写
     
    FileStream fs=File.Open(@"xxx.dat",FileMode.Open);
    BinaryWriter bw=new BinaryWriter(fs););               ->二进制读写
     
    注意,从FileInfo.OpenWrite()返回的FileStream对象被传到BinaryWriter类型的构造函数中。使用这项技术就能很方便地在写入数据前引入一个流。需要理解的是,BinaryWriter构造函数能接受任何派生自Stream类型的参数(比如FileStream、MemoryStream或BufferedStream)。
     
    文本读写器(StreamWriter/Reader)和二进制读写器(Binary Writer/Reader)的关系:
     
    首先两者都是基于流来操作文件进行读写的,但后者是以二进制的方式来进行的,后者写入文件后双击打开会是乱码(该文件后缀为.dat),读写都具体的数据类型来操作流的位置,进行读取的时候要通过具体数据类型读取,例如br.ReadInt32()...;
    文本读写器中写入具体文件中的是字符串的文本格式,双击打开该文件是可以阅读的,同时在运用StreamReader对象进行读取时,就不能以具体类型大小来操作流的位置了,而是以字符大小为单位操作流的位置的,读出来的也是一个字符串,而不是具体类型。
     
    -----------------------正文----------------------
     
    向文件写入非字符类型数据
    当向文件中写入非字符类型的数据时,StreamWriter和BinaryWriter存在巨大差异。
    StreamWriter是把各种类型的数据都转化成字符,然后把字符按照一定的格式编码出来的数据写入文件中。
    BinaryWriter是直接把数据在内存中的真实状态写入到文件中。
    例子:
    class Program{
    static void Main(string[] args){
    FileStream fs = File.Open("E:\MyFile.txt", FileMode.OpenOrCreate, FileAccess.Write);
    StreamWriter sw = new StreamWriter(fs);
    BinaryWriter bw = new BinaryWriter(fs);
    sw.Write(100);
    bw.Write(100);
        }
    }
    用UEdit查看MyFile.txt的16进制码.
            sw的输出为31 30 30,占三个字节。代表了'1', '0', '0'的ASCII码。它输出的全是文本数据。
            bw的输出为64 00 00 00 ,占四个字节。这正是100在内存中的真实状态。int类型占四个字节。
    用记事本打开,sw的输出显示为:"100", bw的输出显示为 "d   ",因为100对应了ASCII码的d。
    BinaryWriter写进去的东西,StreamReader是认不出来的,只能用BinaryReader的对应方法来读取,程序员记住自己是用什么方式写的,然后在用BinaryReader读取时,指定好匹配的编码方式,就可以将原来的数据还原了。
    你当初写进去的是int型,就用BinaryReader.ReadInt32()来读取。例如刚才写进去的100,可以这样读取:
    class Program
    {
    static void Main(string[] args)
    {
    FileStream fs = File.Open("E:\MyFile.txt", FileMode.Open, FileAccess.Read);
    BinaryReader br = new BinaryReader(fs);
    int a = br.ReadInt32();
    }
    }
    这样,a就等于100了
    另外的例子:
    BinaryWriter bw = new BinaryWriter(fs, Encoding.UTF32);
    bw.Write('a');
    输出为:61 00 00 00,占4字节,这就是字符'a'用UTF32格式编码的结果。读取这个输出
    BinaryReader br = new BinaryReader(fs, Encoding.UTF32);
    Console.WriteLine(br.ReadChar());
    BinaryWriter bw = new BinaryWriter(fs, Encoding.ASCII);
    bw.Write('a');
    输出为:61,占1字节,这就是字符'a'用ACSII格式编码的结果。读取这个输出
    BinaryReader br = new BinaryReader(fs, Encoding.ASCII);
    Console.WriteLine(br.ReadChar());
     
    文件的本质:
    所谓的.txt文件,本质不过是硬盘上一堆二进制数据而已。
    往文件中写文本,就是把文本所对应的编码(也就是数字)写进txt文件。
    当你用记事本打开一个txt,就是使用“记事本”这个程序对这堆二进制数据进行解释。
    比方说记事本在txt中读到了一个64H,然后记事本去ASCII字库里查询64H代表什么字符,查到它代表‘d’,于是记事本就负责把‘d’这个字符给显示出来。其实文件里存的不是‘d’,而是‘d’的ASCII编码。
    但问题是,世界上存在多种编码方式,也就对应了不同的字库,比如GBK, 比如Big5, 比如Unicode,同样的编码在不同的字库中对应了不同的字。所以记事本对二进制数据进行解释的时候,需要知道这些数据使用什么方式编码,才能去对应的字库查它是哪个字。所以文件头有时候会有一些标识数据,用来提示记事本这个txt是用什么方式编码。
    比如:文件头的FF FE意味着本文用Unicode格式编码。而FE FF意味着用BigEndianUnicode方式。所以FF FE 8B 73 被解释为'王', FE FF 8B 73被解释为‘譳’。
    我们新建一个文本文档,输入'王',然后查看其16进制的数据,发现文档数据为:CD F5。这是默认编码格式下的'王'的编码。
    然后将该文本文档另存为Unicode格式的,发现其16进制数据变成了:FF FE 8B 73。
    再另存为Unicode big endin之后,16进制数据变为:FE FF 73 8B。
    这些底层数据的变化过程是由记事本程序来维护的,但无论底层数据怎么变动,我们看到的文本都是不变的。记事本程序并负责格式转换并保证只改变编码方式而不改变文本。
    出现乱码,就是对二进制数据进行了错误的解释。
    向文件中写入字符数据时
    当用于写字符的时候,StreamWriter和BinaryWriter是差不多的。二者稍有区别。
    看下面的例子:
    FileStream fs = File.Open("E:\MyFile.txt", FileMode.OpenOrCreate, FileAccess.Write);
    StreamWriter sw = new StreamWriter(fs, Encoding.Unicode);
    sw.Write(‘王’);
    MyFile.txt内容为:FF FE 8B 73
    StreamWriter sw = new StreamWriter(fs, Encoding.BigEndianUnicode);
    sw.Write(‘王’);
    MyFile.txt内容为:FE FF 73 8B
    BinaryWriter bw = new BinaryWriter(fs, Encoding.Unicode);
    bw.Write(‘王’);
    MyFile.txt内容为:8B 73
    BinaryWriter bw = new BinaryWriter(fs, Encoding.BigEndianUnicode);
    bw.Write(‘王’);
    MyFile.txt内容为:73 8B
    当新建的时候,StreamWriter会顺便写入FF FE这样的标识数据,而BinaryWriter不会写入任何表示数据,只把'王'的编码写入文件。
    当append的时候,StreamReader设定的编码方式是不会改变文档原有的编码方式的。
    举例来说。
    有一个空的Unicode格式的MyFile.txt,该txt文件中只有两个字节的数据:FF FE。
    执行如下代码:
        FileStream fs = File.Open("E:\MyFile.txt", FileMode.Append, FileAccess.Write);
           StreamWriter sw = new StreamWriter(fs);
           sw.Write('王');
    执行过程:
    执行之后,MyFile.txt内的数据为:FF FE E7 8E 8B, 其中E7 8E 8B是StreamWriter采用默认编码格式对'王'进行编码的结果。
    当记事本程序试图将FF FE E7 8E 8B解释成文本时,遇到FF FE会认为这是Unicode编码,于是把后边的所有数据都按照Unicode的格式解释,于是E7 8E 8B被解释成了乱码。把FF FE 改成00 00 之后,记事本找不到FF FE,于是就把这一坨数据按照默认方式解释,这就正确地将E7 8E 8B解释成了‘王’字。
     
  • 相关阅读:
    wqy的ACM赛G朱柏庐
    可持久化数据结构
    LibreOJ#2362蚯蚓
    LibreOJ#2359天天爱跑步
    「Luogu2221」[HAOI2012]高速公路
    「Luogu4158」[SCOI2009]粉刷匠
    「Luogu4317」花神的数论题
    WC2019 游记
    最大权闭合子图模型
    「Luogu2762」太空飞行计划问题
  • 原文地址:https://www.cnblogs.com/gaoyangdev/p/12974480.html
Copyright © 2011-2022 走看看