IO操作基本上需要用到Stream相关的子类,因此这类问题在CSDN问得也是比较多。其实对于Stream来说,操作起来比较简单,只要对细节的处理稍微注意一下,相信在使用它的时候也会得心应手。

对于Stream相关的问题,大致分如下几类。

问题一,基本操作的问题;

问题二,编码的问题;

问题三,尾部处理问题;

问题四,Stream缓存问题;

问题五,资源释放问题;

最后一个问题,说说如何使用Stream来更新大文件部分数据。

对于问题一,基本操作的问题,主要是读写问题,主要是出现在文件数据比较大,需要循环写或者读的时候。此时正确读的形式如下。

// Open a file to read

using( FileStream fs = new FileStream( yourFile,

FileMode.Open, FileAccess.Read,

FileShare.None ) )

{

int nRealRead = 0;

byte[] bBuffer = new byte[1024];

do

{

// Read data

nRealRead = fs.Read( bBuffer, 0, 1024 );

// Output data

Debug.WriteLine( Encoding.Default.GetString( bBuffer, 0, nRealRead ) );

}while( nRealRead == 1024 );

}

可是大多数人第一次完成这样操作的时候,都会在“nRealRead = fs.Read( bBuffer, 0, 1024 );”这一句犯错误。认为第二个参数的偏移量对于Stream而设的,所以认为应该用累加的值,也就是目前总共读了多少的字节数。这里需要理解一下Stream的操作,当进行读或者写操作的时候,Stream的游标会根据所读或者所写得字节而自动向前跟进;其次Stream.Read或者Stream.Write这两个方法中第二个参数是针对第一个Buffer参数而言的,而不是对于Stream的,因此不要在这个地方犯错误。

基本问题还牵扯的就是文件打开的方式。有人经常问,如何同时用两个Stream打开同一个文件。其实默认的Stream打开方式是独享的,因此当不指明文件为访问共享的时候,后打开文件操作就会出现异常,因此需要向我上面所写的那样。还有,如果需要指定当前Stream的起始位置,可以通过Seek方法或者设置Position属性来完成。

对于问题二,编码问题。有人使用Stream的子类,例如StreamReader之类来打开一个文本文件,发现读出来的数据是乱码,造成这个原因大多数由于文件中含有中文字符,同时打开文件的时候没有指明编码方式。由于英文和中文的编码方式不同,因此在不指明编码的时候有时会造成读取中文错误。此时只要使用StreamReader类型中含有Encoding参数的构造函数即可,例如:

using( StreamReader sr = new StreamReader( yourFile, Encoding.Default ) )

这里只是采用系统默认的编码方式,但有可能不太适合你文件的编码方式,因此需要在实际应用去调试和变换这个参数。

问题三是,Stream尾部处理问题。此类问题所展现的现象如,复制文件的时候文件会增大。因此在使用Stream.Read和Stream.Write的时候,要通过方法的返回值,来标明真正读和写的字节数,就像前面所写的那样。

// Read data

nRealRead = fs.Read( bBuffer, 0, 1024 );

// Output data

Debug.WriteLine( Encoding.Default.GetString( bBuffer, 0, nRealRead ) );

此时在输出的时候用的不是“1024”,而是“nRealRead”做为字节有效标示。

对于问题四,Stream缓存的问题,这主要表现在写的时候。为了避免频繁操作IO而降低效率,大多数Stream采用异步写的方式,也就是Stream对象要配备有一定的缓存,来暂时保存写的数据。但缓存是有限的,当缓存已满后会造成后续写的数据不能写入,从而导致数据丢失。那么此时需要显示的调用Stream.Flush方法,来把缓存的数据写入到文件中并清空缓存。其实这并不是唯一方法,在一些Stream的子类中还提供了设置BufferSize的方法,或者提供了设置AutoFlush属性来实现自动写入等等,因此这里大家可以根据不同需要而选择不同方法来完成。

对于Stream的释放问题,这可能不单单是使用Stream的问题,可能是使用C#编程而造成的不良习惯。虽说C#的资源是受托管的,但是对于Stream来说,如果不及时释放,那么当其他线程或者进程使用此文件的时候就会造成无法打开的现象(由于Stream大多数都是以独享方式打开),而且没有及时关闭,所占用的Buffer无法及时释放。

因此养成一个好的习惯至关重要。其实释放Stream很简单,要么显示的调用其的Close和Dispose这两个方法,要么使用using程序块,就像我前面所写的那样。

最后一个就是如何使用Stream来更新大文件。比较常见的就是,当文件比较大,但是需要修改的部分很少,因此想要通过Stream直接在某个位置进行类似于删除、插入或者替换等操作。

对于一个文件的更新操作,大致分为三种,这里主要是考虑更新的位置和更新数据长度。

第一种对于文件尾扩展的操作,内容长度不限;

第二种等字节的替换操作,位置不限;

最后一种就是位置不固定,字节数不确定。

上面所说的前两种,进行处理比较简单。对于第一种,只要设置FileMode的时候增加Append标示即可。而对于等字节的替换,就更简单了,直接通过Stream.Seek找到指定的位置,然后调用Stream.Write即可。

而最后一个,是最麻烦的。比较简单的解决方式,创建一个临时文件,然后一边读一边写,遇到需要修改的,先读出来再修改最后再写入。等全部写完了,删除旧文件,修改临时文件的名称为原来名字。

比较麻烦的解决方式,就是通过Share方式,用一个读Stream和一个写Stream直接操纵源文件。这里需要注意的是,为了保证新写的数据不要冲掉还没读出来的数据,也就是说要控制写Stream所写的位置不要超过要读的位置。举例说,目前需要读的位置是文件的800字节处,也就是说800字节以后还没读出来处理,此时写Stream在写完数据后,Stream的位置不能超过800字节,如果写采用的是缓存,那么超过800位置的数据不要立刻通过Flush进行提交。总的来说,通过两个Stream来操作同一个文件,对于这一点要特别注意,处理不好要造成死循环。

C#_Stream的更多相关文章

  1. Java8新特性_stream API 练习

    交易员类 public class Trader { private String name; private String city; public Trader() { } public Trad ...

  2. java8新特性(四)_Stream详解

    之前写过一篇用stream处理map的文章,但是对stream没有一个整体的认识,这次结合并发编程网和ibm中介绍stream的文章进行一个总结,我会着重写对list的处理,毕竟实际工作中大家每天进行 ...

  3. C#高性能TCP服务的多种实现方式

    哎~~ 想想大部分园友应该对 "高性能" 字样更感兴趣,为了吸引眼球所以标题中一定要突出,其实我更喜欢的标题是<猴赛雷,C#编写TCP服务的花样姿势!>. 本篇文章的主 ...

  4. MCDownloadManager ios文件下载管理器

    我们用AFNetworking小试牛刀,写一个简单的下载器来演示功能. 前言 为什么AFNetworking能够成为顶级框架?我们究竟该如何领悟它的精髓所在?这都是很难的问题.安全,高效,流畅,这3个 ...

  5. dicom网络通讯入门(3)

    接下来可以进行消息传递了 ,也就是dimse ,再来复习下 什么是dimse .n-set  n-create c-echo 这些都是dimse  他们都是属于一种结构的pdu 那就是tf-pdu(传 ...

  6. dicom网络通讯入门(2)

    第二篇,前面都是闲扯 其实正文现在才开始,这次是把压箱底的东西都拿出来了. 首先我们今天要干的事是实现一个echo响应测试工具 也就是echo 的scu,不是实现打印作业管理么.同学我告诉你还早着呢. ...

  7. HttpHelper

    /// <summary> /// 类说明:HttpHelper类,用来实现Http访问,Post或者Get方式的,直接访问,带Cookie的,带证书的等方式,可以设置代理 /// 重要提 ...

  8. 第三篇 基于.net搭建热插拔式web框架(重造Controller)

    由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并且http上下文不支持跨域,所以我们要重造一个contro ...

  9. Python之路,Day8 - Python基础 面向对象高级进阶与socket基础

    类的成员 类的成员可以分为三大类:字段.方法和属性 注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段.而其他的成员,则都是保存在类中,即:无论对象的 ...

随机推荐

  1. Sqlserver2008及以上使用全文索引排除干扰词 (转载)

    关于SQLServer2008全文索引干扰词使用SQL SERVER 2008全文索引进行搜索 contains(Keywords, '"xx of xx*"') (注意是后面有包 ...

  2. Sql server的Merge语句,源表中如果有重复数据会导致执行报错

    用过sql server的Merge语句的开发人员都应该很清楚Merge用来做表数据的插入/更新是非常方便的,但是其中有一个问题值得关注,那就是Merge语句中的源表中不能出现重复的数据,我们举例来说 ...

  3. jboss4.2.3 屏蔽响应头server信息

    1.修改配置deploy/jboss-web.deployer/service.xml <Connector port="8080" protocol="HTTP/ ...

  4. Angular-1.6 路由 简单使用

    index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  5. 《SQL Server 2008从入门到精通》--20180717

    目录 1.触发器 1.1.DDL触发器 1.2.DML触发器 1.3.创建触发器 1.3.1.创建DML触发器 1.3.2.创建DDL触发器 1.3.3.嵌套触发器 1.3.4.递归触发器 1.4.管 ...

  6. python web编程CGI

    CGI(通用网关接口),CGI 是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能. CGI 应用程序能与浏览器进行交互,还可通过数据库API 与数据库服务器等外部数据源进行 ...

  7. Software Engineer’s path to the best annual performance review

    http://michaelscodingspot.com/2017/06/04/software-engineers-path-best-annual-performance-review/ How ...

  8. 【工具推荐】截图工具 Snipaste

    0. 说明 [官网介绍] Snipaste 是一个简单但强大的截图工具,也可以让你将截图贴回到屏幕上!下载并打开 Snipaste,按下 F1 来开始截图,再按 F3,截图就在桌面置顶显示了.就这么简 ...

  9. SpringBoot部署

    Spring Boot 部署到服务器 jar 形式 1.打包 若我们在新建Spring Boot 项目的时候,选择打包方式是 jar,则我们只需要用 mvn package 就可以进行打包. 2.运行 ...

  10. DevExpress04、LayoutControl、GalleryControl

    首先需求是通过LayoutControl控件设计下图所示的窗体: 从该界面的设计过程 1.向窗体中添加LayoutControl控件 在将该控件拖入窗体后,最好立即设置该控件的尺寸和位置.拖入后,在如 ...