(转载)NET流操作

简介: http://www.oseye.net/user/kevin/blog/86 概念 数据流(Stream)是对串行传输数据的一种抽象表示,是对输入/输出的一种抽象。数据有来源和目的地,衔接两者的就是串流对象。

http://www.oseye.net/user/kevin/blog/86

概念

数据流(Stream)是对串行传输数据的一种抽象表示,是对输入/输出的一种抽象。数据有来源和目的地,衔接两者的就是串流对象。用比喻的方式来说或,数据就好比水,串流对象就好比水管,通过水管的衔接,水由一端流向另一端,如下图所示:

从应用程序的角度来说,如果将数据从来源取出,可以试用输入(读)串流,把数据储存在内存缓冲区;如果将数据写入目的地,可以使用输出(写)串流,把内存缓冲区的数据写入目的地:

当希望通过网络传输数据,或者对文件数据进行操作时,首先需要将数据转化为数据流。典型的数据流和某个外部数据源相关,数据源可以是文件、外部设备、内存、网络套接字等。根据数据源的不同,.能Net提供了多个从Stream类派生的子类,每个类代表一种具体的数据流类型,如何磁盘文件直接相关的文件流类FileStream,和套接字相关的网络流类NetworkStream,和内存相关的内存流类MemoryStream等。


流具有如下3种基本操作:

  • 写入:将数据从内存缓冲区传输到外部源;
  • 读取:将数据从外部源传输到内存缓冲区;
  • 查找:重新设置流的当前位置,以便随机读写。但并不是所有的流类型都支持查找,如网络流类没有当前位置的概念,就不支持查找。

Stream是虚拟类,它以及它的派生类都提供了Read和Write方法,可以支持在字节级别上对数据进行读写。Read方法从当前字节流读取字节放至内存缓冲区,Write方法把内存缓冲区的字节写入当前流中。


封装的其他流类

但仅支持字节级别的数据处理会给开发人员带来不便。将定应用程序需要将字符数据写入到流中,则需要先将字符数据转化为字节数组之后才能调用Write方法写入流。因此,除了Stream及其派生类的读写方法之外,.Net框架同样提供了其他多种支持流读写的类:

  • BinaryReader:从Streams读取编码的字符串和基元数据类型;
  • BinaryWriter:向Streams写入编码的字符串和基元数据类型;
  • StreamReader:通过使用Encoding将字符转换为字节,向Streams写入字符;
  • StreamWriter:通过使用Encoding进行字符和字节的转换,可直接从Streams中读取字符;

一、文件流 FileStream

FileStream流继承与Stream类,一个FileStream类的实例实际上代表一个文件流,使用FileStream类可以对文件系统上是文件进行读取、写入、打开和关闭操作。

创建FileStream实例

1、.Net提供多种获取FileStream对象的方法,其中构造函数就有10多种,我们看下典型的构造函数顺便讲解下参数含义:

  1. publicFileStream(string path,FileMode mode,FileAccess access);

参数说明:
path指明文件所在的路径信息;
mode是FileMode的枚举值,表示文件打开或创建的方式,含义如下:

  • CreateNew:指定操作系统应创建新文件,如果文件已经存在,则引发IOException;
  • Create:指定操作系统应创建新文件,如果文件已经存在,它将被覆盖;
  • Open:指定操作系统应打开现有文件,如果文件不存在,则引发FileNotFoundException;
  • OpenOrCreate:指定操作系统应打开文件,如果文件不存在,则创建新文件;
  • Truncate:指定操作系统应打开现有文件,文件一旦打开,就将截断为零字节大小;
  • Append:打开先有文件并把Position设置至文件尾,如果文件不存在将创建新文件。Append只能同FileAccess.Write一起使用;

access是FileAccess的枚举值,它控制对文件的访问权限,含义如下:

  • Read:打开文件用于只读;
  • Write:打开文件用于只写;
  • ReadWrite:打开文件,用于读写;

2、除了FileStream类本身提供的构造函数外,System.IO命名空间下的File和FileInfo类也提供了创建FileStream对象的方法。其中OpenRead方法返回只读文件流,OpenWrite方法返回只写文件流。如:

   FileStream fs=File.OpenRead(@"c:\file.txt");

FileStream的读写方法:

public override intRead(
byte[] array,//内存缓冲区,储存从文件流中读取的数据
int offset,//array开始写入数据的下标值
int count //从文件流中读取的字节大小
);
 
public override void Write(
byte[] array,//内存缓冲区,存储了要写入流的字节数据
int offset,// 从array的下标值开始取数据
int count //要写入的字节数
);

 

FileStream实例 

try
{
//写入
FileStream fileStream =newFileStream(@"d:\test.txt",FileMode.OpenOrCreate);
byte[] content =Encoding.UTF8.GetBytes("我爱我家");
fileStream.Write(content,0, content.Length);
fileStream.Position=0;//设置当前位置
content =Encoding.UTF8.GetBytes("我爱你家");
 
fileStream.Write(content,0, content.Length);
fileStream.Close();
 
//读取
fileStream =newFileStream(@"d:\test.txt",FileMode.Open);
content =newbyte[fileStream.Length];
fileStream.Read(content,0, content.Length);
Console.WriteLine(Encoding.UTF8.GetString(content));
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}

 


二、内存流 MemoryStream

和文件流布同,MemoryStream类表示的是保存在内存中的数据流,由内存流封装的数据可以在内存中直接访问。内存一般用于暂时缓存数据以降低应用程序对临时缓冲区和临时文件的需要。

既然字节数据也在内存中存储,为什么还要引入内存流的概念呢?这是因为内存流和字节数组虽然都位于程序缓冲区,但是具有不同特性。内存流相对于字节数组而言,具有流特有的特性,并且容量可自动增长,在数据加密以及对长度不定的数据进行缓存等场合,使用内存流比较方便。

创建MemoryStream实例

MemoryStream有多种构造函数,部分举例如下:

  • public MemoryStream();该构造函数初始分配的容量大小为0,随着数据的不断写入,其容量可以不断地自动扩展。
  • public MemoryStream(byte[] buffer);根据字节数组buffer初始化,实例的容量大小规定为字节数组的长度。
  • public MemoryStream(int capacity);容量固定为capacity。

MemoryStream实例

MemoryStream mem =newMemoryStream();
Console.WriteLine("初始分配的容量:"+mem.Capacity+" 初始使用量:"+mem.Length);
byte[] content =Encoding.UTF8.GetBytes("我爱我家");
mem.Write(content,0, content.Length);
Console.WriteLine("初始分配的容量:"+ mem.Capacity+" 初始使用量:"+ mem.Length);

 


三、网络流NetWorkStream

网络流的意思是数据在网络的各个位置之间是以连续的字节形式传输的,NetWorkStream只能用于面向连接的套接字。

对于NetWorkStream流,写入操作是指从来源端内存缓冲区到网络上的数据传输;读取操作是从网络上到接收端内存缓冲区的数据传输。

 

一、StreamWriter和StreamReader

从上一篇博文可知文件流、内存流和网络流操作的都是字节,每次都要进行字节序列的转换,所以使用上比较麻烦。StreamWriter和StreamReader在对Stream底层进行了封装,可以直接操作字符数据。

StreamWriter类主要完成一种特定的编码从流中读取字符的功能,它的构造函数和常用方法如下:

  • StreamWriter(Stream stream),构造函数,StreamWriter不仅能对FileStream对象,而且能够对NetWorkStream、MemoryStream等继承了Stream类的流对象进行封装;
  • StreamWriter(string path),构造函数,如需要处理的是文件流,则可直接利用文件路径创建以UTF8编码的StreamWriter对象;
  • Write(string value),方法,向数据流写入数据;
  • WriteLine(string value),方法,向数据流写入数据,并追加一个换行符(Unix)或回车换行符(Windows);
  • Close(),方法,关闭流,释放资源;

StreamReader类主要以特定的编码向流中写入字符,它的构造函数和常用方法如下:

  • StreamReader(Stream stream),构造函数,利用流对象创建StreamReader对象;
  • StreamReader(string path),构造函数,如需要处理的是文件流,则可直接利用文件路径创建以UTF8编码的StreamReader对象;
  • string ReadLine(),方法,读取数据直到遇到换行符(Unix)或回车换行符(Windows);
  • string ReadToEnd(),方法,读取到文件尾的全部数据
  • int Peek(),方法,返回数据中下一个可用字符的编码值,如到达文件末尾则返回-1;
  • Close(),方法,关闭流,释放资源;

StreamWriter和StreamReader实例

try
{
//写入
StreamWriter sw =newStreamWriter(@"d:\abc.txt");
sw.WriteLine("我爱我家");
sw.Close();
 
//读取
StreamReader sr =newStreamReader(@"d:\abc.txt");
Console.WriteLine(sr.ReadToEnd());
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}

 

二、BinaryReader和BinaryWriter

为了操作图像、压缩文件等二进制流文件,System.IO还提供了BinaryReader类和BinaryWriter类,用于二进制模式的读写流。
BinaryReader的每个读方法都有一个对应的写方法,比如针对不同的数据结构,BinaryReader类提供了ReadByte、ReadBoolean、ReadInt、ReadInt16、ReadString等,与之对应的BinaryReader类则提供了多个重载的Write方法,分别对应上面的读方法,所以使用起来非常方便。例如,当Write方法传递的参数是Int32类型时,利用BinaryWriter的Write方法可用将Int32类型数据转化为长度为4的字节数组,并将字节流传递给一个Stream对象。

BinaryReader和BinaryWriter实例

byte[] sendData;
using (MemoryStream mem =newMemoryStream()){
BinaryWriter writer =newBinaryWriter(mem,Encoding.UTF8);
writer.Write(SocketTools.strConvertToHexByte(SocketTools.md5(packageHead.CarIdentifier,32)));
writer.Write(packageHead.ProjectIdentifier);
writer.Write(packageHead.ModelIdentifier);
writer.Write(packageHead.ProtocolVersion);
writer.Write(SocketTools.reverseShort(packageHead.RequestSerial));
writer.Write(SocketTools.reverseInt32(packageHead.PacketTimestamp));
writer.Write(SocketTools.reverseShort(packageHead.FunctionNo));
writer.Write(SocketTools.reverseShort(packageHead.DataLength));
writer.Write(msg.ToCharArray());
sendData = mem.ToArray();
writer.Close();
}

 

相关文章
|
数据库
理解 .NET Core中的Channel篇之二——高级通道
  1、复习   在我们以前的文章中,我们看了一些关于Channels如何工作的简单示例,并且看到了一些漂亮的功能,但是在大多数情况下,它与任何其他Queue实现都非常相似。   因此,让我们深入探讨一些更高级的主题。   好吧,虽然说得高级,但是很多事情还是很简单。不过,为了获取更多有价值的信息,还是值得我们去探索的!   2、读写分离
212 0
|
.NET 分布式数据库 API
.NET中的异步编程- IO完“.NET技术”成端口以及FileStream.BeginRead
  写这个系列原本的想法是讨论一下.NET中异步编程风格的变化,特别是F#中的异步工作流以及未来的.NET 5.0中的基于任务的异步编程模型。但经过三篇文章后很多人对IO异步背后实现的原理以及为什么这样能提高性能很感兴趣。
821 0
|
Web App开发 缓存 .NET
一起谈.NET技术,全面认识一下.NET 4的缓存功能
  很多关于.NET 4.0新特性的介绍,缓存功能的增强肯定是不会被忽略的一个重要亮点。在很多文档中都会介绍到在.NET 4.0中,缓存功能的增强主要是在扩展性方面做了改进,改变了原来只能利用内存进行缓存的局限,允许用户在不改变代码的情况下通过修改配置的方式,灵活的切换缓存介质。
1183 0
|
.NET 分布式数据库 API
一起谈.NET技术,.NET中的异步编程- IO完成端口以及FileStream.BeginRead
  写这个系列原本的想法是讨论一下.NET中异步编程风格的变化,特别是F#中的异步工作流以及未来的.NET 5.0中的基于任务的异步编程模型。但经过三篇文章后很多人对IO异步背后实现的原理以及为什么这样能提高性能很感兴趣。
817 0
|
.NET 分布式数据库 API
.NET中的异步编程- IO完成端口以及FileStream.“.NET研究”BeginRead
  写这个系列原本的想法是讨论一下.NET中异步编程风格的变化,特别是F#中的异步工作流以及未来的.NET 5.0中的基于任务的异步编程模型。但经过三篇文章后很多人对IO异步背后实现的原理以及为什么这样能提高性能很感兴趣。
825 0
|
.NET API 分布式数据库
一起谈.NET技术,.NET异步编程:IO完成端口与BeginRead
  写这个系列原本的想法是讨论一下.NET中异步编程风格的变化,特别是F#中的异步工作流以及未来的.NET 5.0中的基于任务的异步编程模型。但经过前几篇文章(为什么需要异步,传统的异步编程,使用CPS及yield实现异步)的发表后,很多人对IO异步背后实现的原理以及为什么这样能提高性能很感兴趣。
1145 0
|
.NET 分布式数据库 API
.NET异步编程:IO完成端口与“.NET研究”BeginRead
  写这个系列原本的想法是讨论一下.NET中异步编程风格的变化,特别是F#中的异步工作流以及未来的.NET 5.0中的基于任务的异步编程模型。但经过前几篇文章(为什么需要异步,传统的异步编程,使用CPS及yield实现异步)的发表后,很多人对IO异步背后实现的原理以及为什么这样能提高性能很感兴趣。
1039 0
.NET简谈面“.NET技术”向接口编程
  过程式的开发方式已逐渐退出大众的眼线,随之而来的是各种各样的高抽象的开发模式;我们不得不承认在没有设计模式的时候,我们很难总结出有价值的开发模型,便于以后重复使用和推广;面向对象的流行,让我们开发人员重新站在一个高的起点来看待软件模型,抽象固然是好事,但是也给初学者带来了迷惑,将软件中的东西都想成很简单的封装,我们只需要调用就行,这样越来越多的开发人员开始慢慢的往上浮,有一定编程经验和感触的人,能够明白我所说的浮,也算是给初学者提个醒吧。
905 0
.net reactor 学习系列(三)---.net reactor代码自动操作相关保护功能
原文:.net reactor 学习系列(三)---.net reactor代码自动操作相关保护功能         接上篇,上篇已经学习了界面的各种功能以及各种配置,这篇准备学习下代码控制许可证。
1199 0
|
数据安全/隐私保护 安全 .NET
.net reactor 学习系列(二)---.net reactor界面各功能说明
原文:.net reactor 学习系列(二)---.net reactor界面各功能说明         安装了.net reactor之后,可以在安装目录下找到帮助文档REACTOR_HELP.chm,目前没有中文版本,里面详细介绍了.net reactor的各功能及使用场景。
1237 0