C#中的输入输出流
流是通过通信路径传递的字节序列,C#采用流模型读写文件、网络或内存中的数据。
有两个主要的流:输入流和输出流。输入流用于读取数据(读操作),输出流用于写入数据(写操作)
C#中关于流的常用类,这些类大多属于System.IO命名空间的类:
创建流的类:
Stream:所有流的基类,定义流的基本操作,如Read和Write等方法,是抽象类,无法直接使用 FileStream:以文件作为数据源的流,可以用来读写文件 NetworkStream:以网络作为数据源的流,可以通过此流发送或接受网络数据 MemoryStream:以内存作为数据源的流,可以用来随机读写内存中的数据 BufferedStream:以缓存区作为数据源的流,可以用来读写缓存区中的数据 读写流的类: TextReader:字符读取的基类,定义基本字符读取操作,是抽象类,无法直接使用 TextWriter:字符写入的基类,定义基本字符写入操作,是抽象类,无法直接使用 StreamReader:从流读取字符 StreamWriter:将字符写入流 StringReader:用于读取字符 StringWriter:用于写入字符 BinaryReader:以二进制的形式从流读取数据 BinaryWriter:以二进制的形式将数据写入流
对于流操作可能用到的其他类:
File:用于方便的创建文件流 Directory:有助于操作目录结构 DirectoryInfo:用于对目录执行操作 DriveInfo:提供驱动器的信息 FileInfo:用于对文件执行操作 Path:对路径信息执行操作
文件流的创建和操作,查看官方文档
FileStream(String, FileMode [, FileAccess, FileShare, Int32, FileOptions]) 可选参数可以从后向前省略 FileMode枚举定义了各种打开文件的方法: Append:打开文件,定位在末尾。如果不存在,则创建 此方法只能以FileAccess.Write只写模式打开文件 Open:打开文件。定位在开头,如果不存在,则抛出异常 OpenOrCreate:打开文件,定位在开头,如果不存在,则创建 定位在开头打开文件写入的话会替换文件内容 Create:创建新文件。如果已存在,则删除并创建新文件 CreateNew:创建新文件。如果已存在,则抛出异常 Truncate:打开文件,并将文件内容截断为零字节大小,保留文件的初始创建日期。如果不存在,则抛出异常 FileAccess枚举成员Read、ReadWrite和Write FileShare用于设定文件锁,枚举成员: Inheritable:允许文件句柄可由子进程继承。Win32不直接支持此功能 None:关闭文件前不允许任何程序打开文件 Read:关闭文件前允许任何程序读取文件 ReadWrite:关闭文件前允许任何程序读写文件 Write:关闭文件前允许任何程序写入文件 Delete:关闭文件前允许任何程序删除文件 Int32:设定文件缓存区大小 FileOptions枚举用于创建FileStream对象的高级选项,成员有: Asynchronous:指示文件可用于异步读取和写入 DeleteOnClose:指示当不再使用某个文件时,自动删除该文件 Encrypted:指示文件是加密的,只能通过用于加密的同一用户帐户来解密 None:指示在生成 FileStream 对象时,不应使用其他选项。 RandomAccess:指示随机访问文件 SequentialScan:指示按从头到尾的顺序访问文件 WriteThrough:指示系统应通过任何中间缓存、直接写入磁盘。
另外创建FileStream文件流也可以使用File类中的方法:
Open(String, FileMode [, FileAccess] [, FileShare]):返回FileStream文件流 FileAccess默认为ReadWrite,FileShare默认为None File.OpenRead(file):只读方式创建文件流 File.OpenWrite(file):只写方式创建文件流
当一个文件流创建完成后,我们可以使用文件流的一些方法来操作文件流:
CanRead:流是否支持读取,只读属性 CanWrite:流是否支持写入,只读属性 CanSeek:流是否支持定位,只读属性 CanTimeout:当前流是否可以超时 Name:已打开的文件的绝对路径,只读属性 SafeFileHandle :文件安全句柄,只读属性 Position:当前的指针位置 ReadTimeout:读取或设置读取超时时间(毫秒) WriteTimeout:读取或设置写入超时时间(毫秒) Length:流的长度(字节大小) IsAsync:是否是同步工作(还是异步工作) Read(byte[]array,int offset,int count):从流中读取数据,返回读取的字节数 offset表示读取时的偏移量(从0开始) count表示读取多少个字节 读取到的数据会保存在array中 使用Encoding.GetString(array)可以将字节数组转化为指定编码的字符串 BeginRead(byte[]array,int offset,int count,callback_func,args):异步读取 每一次使用BeginRead()方法事都必须要使用EndRead()方法结束异步状态 最后的args是要传给读取完毕后的回调函数callback_func时传入的参数 一般传入调用BeginRead()方的对象,用于在回调函数用调用EndRead()方法 EndRead():异步读结束 Write(byte[]array,int offset,int count):将字节块(字节数组)写入流 将array中的数据写入到流 offset表示从array哪个索引开始写入 count表示写入多少个字节 使用Encoding.GetBytes(string)可以将字符串以指定编码转化为字节数组 Encoding是一个字符编码值,位于System.Text命名空间: 可以通过System.Text.Encoding.GetEncoding(encoding)来获取指定编码值(不区分大小写) System.Text.Encoding.GetEncoding("gb2312") System.Text.Encoding.GetEncoding("utf-8") System.Text.Encoding.UTF8:预设的UTF-8编码值 System.Text.Encoding.Default:自动检测计算机默认编码 BeginWrite(byte[]array,int offset,int count,callback_func,args):异步写 每一次使用BeginWrite()方法事都要带上EndWrite()方法 EndWrite():异步写结束 CopyTo(Stream[, Int32]):从当前流中读取字节并将其写入到另一流中,第二个参数为缓冲区大小 CopyToAsync(Stream[, Int32]):从当前流中异步读取字节并将其写入到另一流中 Seek(longoffset,SeekOrigin origin):设置文件读取或写入的指针位置 SeekOrigin是一个枚举,成员有Begin、Current(当前位置)、End longoffset表示从指定位置向后移动多少位开始读写,可以为负数 SetLength(length):截取FileStream内容为指定长度 Lock(long position,long length) 够锁住文件中的某一部分,能够精确锁定住需要锁住的文件的部分内容 Unlock (long position,long length):用于文件部分枷锁后的解锁 Flush(Boolean):清除该流缓冲区,使得所有缓冲的数据都被写入的文件中,参数表示同时清除所有中间文件缓冲区 FlushAsync():异步清除此流的所有缓冲区并导致所有缓冲数据都写入基础设备中 Close():关闭当前流并释放与之关联的所有系统资源
读写流
文件流对象操作的是最底层的字节级数据。因为它比较底层的原因,它可以读取任何文件,但是操作起普通文本文件显得很笨拙。
StreamReader类是专门用于读写文本文件流的类,从底层从底层封装了文件流,提供更便捷的读写文件方式,不需要麻烦的编解码操作
读取流,官方文档
创建读取流: StreamReader(FileStream , Encoding) StreamReader(file_string , Encoding , auto_encoding , bufferSize) auto_encoding表示是否自动检测文件编码,如果设置为true,则会忽略第二个Encoding参数 bufferSize表示设置缓冲区大小 File.OpenText(file_string):以UTF-8编码返回StreamReader流 BaseStream:获取其内部的文件流对象 Encoding:获取字符编码 CurrentEncodin:获取当前StreamReader的Encoding EndOfStream:判断StreamReader是否已经处于当前流的末尾 Read():获取当前char的下个char并使指针指向下个字符,当返回值是-1时,表示已经到文件末尾了 Peek():功能同Read(),但是使用后指针不会向后移动 Read(char[] array,int offset,int count):从流中读取数据并保存如char数组array,返回读取的字节数 ReadBlock(char[] array,int offset,int count) :功能同Read(),区别是从效率上来说ReadBlock更高点,而且ReadBlock并非属于线程安全 ReadLine():从流中读取一行并且指针下移一行,返回字符串 ReadToEnd():读取流从当前位置到最后的所有数据,返回字符串 Close():关闭StreamRead内置的文件流
写入流,官网文档:
创建写入流: StreamWriter(FileStream,Encoding,bufferSize) StreamWriter(file_string,[is_append=false] [,Encoding]) 默认会删除并新建文件,传入第二个参数true则为追加模式 File.AppendText(file_string) File.CreateText(file_string) BaseStream:获取其内部的文件流对象 Encoding:获取字符编码 NewLine:获取当前系统的默认换行字符 AutoFlush:该属性设定每次使用Write()方法后直接将缓冲区的数据写入文件 Write(data):写入data数据 data可以是一个字符串,也可以是一个布尔值、一个字符、一个十进制数字、一个8字节浮点数、一个4字节有符号的整数 WriteLine(data):向流写入一行并在最后附加一个换行符,data可以是任何常见的数据类型 Flush():清空缓冲区 Close():关闭StreamWriter内置的文件流
内存流,官方文档
MemoryStream是内存流,为系统内存提供读写操作,由于MemoryStream是通过无符号字节数组组成的,可以说MemoryStream的性能可以
算比较出色,所以它担当起了一些其他流进行数据交换时的中间工作,同时可降低应用程序中对临时缓冲区和临时文件的需要,在很多场合我们必须使用它来提高性能
创建内存流 MemoryStream(byte[] array [, int index, int count]):创建内存流对象,并将array在初始化时写入流 MemoryStream(byte[] array,bool writeable):Writeable参数定义该流是否可写 MemoryStream(int capacity):内存流的可用内存大小,默认为512M(512*1024*1024) MemoryStream(byte[] byte,int index, int count, bool writeable, bool publiclyVisible) publiclyVisible表示是否可以启用GetBuffer方法,它返回无符号字节数组,流从该数组创建 Capacity:内存流可使用的容量 GetBuffer():这个方法使用时需要小心,因为这个方法返回无符号字节数组,也就是说,即使我只输入几个字符例如”HellowWorld”我们只希望返回11个数据就行,可是这个方法会把整个缓冲区的数据,包括那些已经分配但是实际上没有用到的字节数据都返回出来 WriteTo(Stream stream):memoryStream常用起中间流的作用,读写在处理完后将内存流写入其他流中
缓存区流,官方文档
缓冲区是内存中的一块连续区域,用来缓存或临时存储数据,也就是说流可以通过缓冲区逐步对数据进行读取或写入操作,
BufferedStream中的缓存区可以由用户设定,其表现形式为byte数组
假如非固态硬盘没有缓冲区,如果下载一个文件,磁头的读写是非常的频繁,直接的结果是磁头寿命急剧减少,甚至将硬盘直接烧毁或者损坏
MemoryStream同样也是在内存中对流进行操作,和BufferedStream有什么区别呢? BufferedStream并不是将所有内容都存放到内存中,而MemoryStream则是。 BufferedStream必须跟其他流如FileStream结合使用,而MemoryStream则不用 创建缓存区流 BufferedStream(Stream [, Int32]):第二个参数时设置缓冲区大小,默认为4096字节 缓存区流的属性和方法参考Stream类
网络流,官方文档
NetworkStream类的命名空间为System.Net.Sockets,这和之前的流不同
NetworkStream是面向连接的,只能用在具有Tcp/IP协议之中,如果用在UDP中编译不报错,会报异常
NetworkStream必须借助Socket (也称之为流式socket),或使用一些返回值,如TcpClient类的GetStream方法
可以把Socket理解Tcp/IP协议的抽象,并且能够实现Tcp/IP协议栈的工具,可以利用Socket实现客户端和服务端双向通信
举个例子,女友打电话给我,我可以选择连接,或者拒绝,如果我接了她的电话,我和她通过电话连接(Connect),那电话就是“Socket”,女友和我
都可以是客户端或服务端,只要点对点就行,我们的声音通过电话传递,但是具体传输内容不归Socket管辖范围
Socket的直接任务可以归纳为以下几点:
创建客户端或服务端 发送accept 信息给对方,表示两者已经建立连接,并且可以互相传递信息了(Send) 创建点对点的连接(Connect) 服务端或客户端监听是否有服务端或客户端传来的连接信息(Listening) 具体发送和接收什么信息内容不是Socket管辖的范围,但是必须是Socket进行发送和接收的动作
简单介绍下TcpClient,TcpListener,IPEndPoint类的作用
TcpClient:是微软基于Tcp封装类,用于简化Tcp客户端的开发,主要通过构造带入主机地址或者IPEndPonint对象,然后调用Connect进行和服务器点对点的连接,连接成功后通过GetStream方法返回NetworkStream对象 TcpListener:也是微软基于Tcp封装类,用于监听服务端或客户端的连接请求,一旦有连接请求信息,立刻交给TcpClient的AcceptTcpClient方法捕获,Start方法用于开始监听 IPEndPonint:处理IP地址和端口的封装类 IPAddress:提供包含计算机在 IP 网络上的地址的工具类
创建网络流
NetworkStream(Socket):为指定的Socket创建NetworkStream类的新实例 NetworkStream(Socket, Boolean ownsSocket):ownsSocket设置NetworkStream是否拥有该Socket所有权 NetworkStream(Socket, FileAccess):用指定的访问权限为指定的Socket创建NetworkStream类的新实例 NetworkStream(Socket, FileAccess, Boolean ownsSocket) 如果该NetworkStream拥有对Socket的所有权,则在使用NetworkStream的Close方法时会同时关闭Socket 基于安全上的考虑NetworkStream不支持Posion属性和Seek方法
网络流的属性
CanSeek :用于指示流是否支持查找,它的值始终为false DataAvailable:判断NetworkStream上是否有可用的数据
网络流的方法
同样,NetworkStream的方法大致重写或继承了Stream的方法 Read(byte[] buffer,int offset,int size):将数据读入buffer并返回成功读取的字节数 如果没有可以读取的数据,则 Read 方法返回0 Read操作将读取可用数据直至达到由size参数指定的字节数为止 如果远程主机关闭了连接并且已接收到所有可用数据,Read方法将立即完成并返回零字节 Write(byte[] buffer, int offset,int size) 方法将一直处于阻塞状态(可以用异步解决),直到成功发送了自定字节数的数据为止