原文:http://www.cnblogs.com/ybwang/archive/2010/06/12/1757409.html

参考:

1. 《C#高级编程》第六版

2.  文件流和数据流-C#程序设计教程

2010-7-11补充:

发现了一篇讲编码的深入而全面的好文章http://www.cnblogs.com/KevinYang/archive/2010/06/18/1760597.html

向文件写入非字符类型数据

当向文件中写入非字符类型的数据时,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解释成了‘王’字。

分类: c#基础

绿色通道: 好文要顶 关注我 收藏该文与我联系

ybwang1989
关注 - 9
粉丝 - 25

+加关注

5

0

(请您对文章做出评价)

«上一篇:[学习笔记]c#中的委托、事件、Func、Predicate、Observer设计模式以及其他
»下一篇:[学习笔记].NET中的内存分析

posted on 2010-06-12 21:52 ybwang1989 阅读(4712) 评论(2) 编辑 收藏

评论

#1楼 2012-04-08 17:11行一山人

好文,赞一个!

支持(0)反对(0)

#2楼 2012-06-05 08:27Brad Xue

好文,基础。

支持(0)反对(0)

C#中StreamWriter与BinaryWriter的区别兼谈编码。的更多相关文章

  1. (转)java中equals和等号(==)的区别浅谈

    java中的数据类型,可分为两类:1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boolean   他们之间的比较,应用双等号(==) ...

  2. 转 浅谈C++中指针和引用的区别

    浅谈C++中指针和引用的区别 浅谈C++中指针和引用的区别   指针和引用在C++中很常用,但是对于它们之间的区别很多初学者都不是太熟悉,下面来谈谈他们2者之间的区别和用法. 1.指针和引用的定义和性 ...

  3. using中StreamWriter XmlWriter 区别

    使用StreamWriter using (var writer = new StreamWriter(File.Create(path))) { writer.WriteLine("sdf ...

  4. C#中is与as的区别分析

    这篇文章主要介绍了C#中is与as的区别,较为详细的分析了is与as的原理与特性及用法区别,具有很好的学习借鉴价值,需要的朋友可以参考下 本文实例分析了C#中is与as的区别,分享给大家供大家参考.具 ...

  5. 浅谈c#的三个高级参数ref out 和Params C#中is与as的区别分析 “登陆”与“登录”有何区别 经典SQL语句大全(绝对的经典)

    浅谈c#的三个高级参数ref out 和Params   c#的三个高级参数ref out 和Params 前言:在我们学习c#基础的时候,我们会学习到c#的三个高级的参数,分别是out .ref 和 ...

  6. 【转】为什么我们都理解错了HTTP中GET与POST的区别

    GET和POST是HTTP请求的两种基本方法,要说它们的区别,接触过WEB开发的人都能说出一二. 最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数. 你可能自己 ...

  7. Visual Studio 中 Build 和 Rebuild 的区别

    因为之前写的程序比较小,编译起来比较快,所以一直都没有太在意 Build 和 Rebuild 之间的区别,后来发现两个还是有很大不同. Build 只针对在上次编译之后更改过的文件进行编译,在项目比较 ...

  8. SQL Server 中 EXEC 与 SP_EXECUTESQL 的区别

    SQL Server 中 EXEC 与 SP_EXECUTESQL 的区别 MSSQL为我们提供了两种动态执行SQL语句的命令,分别是 EXEC 和 SP_EXECUTESQL ,我们先来看一下两种方 ...

  9. java集合中List与set的区别

       java集合中List与set的区别.     List可以存储元素为有序性并且元素可以相同.     set存储元素为无序性并且元素不可以相同.     下面贴几段代码感受一下: ArrayL ...

随机推荐

  1. <p>1、查询端口号占用,根据端口查看进程信息</p>

    2017年6月份的时候,我就着手在公司推广git,首先我自己尝试搭建了GitLab来管理代码,并且通过以下博客记录了GitLab的搭建,以及GitLab备份,GitLab升级等事情. git学习——& ...

  2. HDU 4004 二分

    The Frog's Games Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others) ...

  3. C primer plus 5 读书笔记3

    1.ASCII 码前31位控制字符(非打印字符)的表示方法:(1)使用ASCII码表示比如蜂鸣字符用7(十进制)表示:(2),使用特殊的符号序列,即转义序列.如 ‘\a’表示蜂鸣字符.(3),使用十六 ...

  4. win32程序应用mfc库

    引入<afx.h> 此时会出现如下错误: #ifdef _DLL#ifndef _AFXDLL#error Building MFC application with /MD[d] (CR ...

  5. C语言中的可变参数函数

    C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为: int printf( const char* format, ...); 它除了有一个参数format固定以外 ...

  6. poj3666&&bzoj1592

    题解: 和bzoj1367差不多 然后a[i]-i不用加 然后我再另一个地方加了这句话 然后poj ac,bzoj wa poj数据水啊 代码: #include<cstdio> #inc ...

  7. Mysql04

    mysql: dbs 数据库系统 bdms 数据库管理系统 bda 数据库管理员 db 数据库 dba通过dbms来操作db! 关系型数据库和非关系型数据库 登录mysql mysql -h主机地址 ...

  8. 在JavaScript中进行文件处理,第二部分:文件读取

    译注:原文是<JavaScript高级程序设计>的作者Nicholas Zakas写的,本翻译纯属为自己学习而做,仅供参考.原文链接:这里 在我的前一篇blog中,我介绍了在JavaScr ...

  9. js中的reduce()函数

    1. 首先看下语法如下 2 . 写了个demo如下 var fa = [1,2,3,4] function red(a, b) { console.log(arguments); return a + ...

  10. threading模块创建线程

    什么是线程 (thread) 线程也是一种多任务编程方式,可以使用计算机的多核资源.线程被称为轻量级的进程. 线程特征 *线程计算机多核分配的最小单位 *一个进程可以包含多个线程 *线程也是一个运行的 ...