首页 > C# > C#中的输入输出流
2021
10-11

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)
    方法将一直处于阻塞状态(可以用异步解决),直到成功发送了自定字节数的数据为止
最后编辑:
作者:qingheluo
这个作者貌似有点懒,什么都没有留下。

留下一个回复

你的email不会被公开。