C#学习笔记 流

在.NET Framework中进行的所有的输入和输出工作都要使用到流(stream)。流是序列化设备(serial device)的抽象表示。序列化设备可以以线性方式存储数据,并可以按同样的方式访问:一次访问一个字节。
有两种类型的流:

  • 输出流: 当向某些外部目标写入数据时,就要用到输出流。这可以是物理磁盘文件、网络位置、打印机或另一个程序。
  • 输入流: 用于将数据读入程序可以访问的内存或变量中。到目前为止,我们使用的最常见的输入流形式是键盘。
    流的相关接口和类主要位于System.IO命名空间,其中Stream类是抽象类,它有三个重要子类分别针对的是不同的存取对象:FileStream类表示文件操作,MemoryStream表示内存操作,BufferedStream类表示缓冲处理。

1.Stream类

抽象的Stream类包含了流中所需要的许多属性和方法,如下表:
在这里插入图片描述
在这里插入图片描述
这些属性和方法中涉及了流的读写的各个方面。读写操作的4个方法如下:

	int ReadByte();
	int Read(byte[] array, int offset, int count);
	void WriteByte(byte value);
	void Write(byte[] array, int offset, int count);

其中ReadByte()将读入的字节转成整数并返回,如果没有读到字节,则返回-1Read()方法返回所读字节的数目。
通过BeginRead()EndRead()BeginWrite()EndWrie()等方法,Stream类可以支持异步I/O 操作。
Seek()方法表示在流中对搜索指针进行定位,用来决定下一步的读或写操作的位置。在这样的流中,其CanSeek属性值为true,并且可以使用其Seek()方法来设定指针的位置。Seek()方法需要两个参数:一个用来表示搜索指针移动距离的数值,另一个用来确定指针移动的参照位置。参照位置是SeekOrigin的枚举成员,可以是以下3中情况之一:

  • SeekOrigin.Begin(文件的开头);
  • SeekOrigin.Current(文件中指针的当前位置);
  • SeekOrigin.End(文件的结尾)。
    下面代码展示了如何在文件中进行搜索处理:
	aStream.Seek(200, SeekOrigin.Begin);	// 从开头移到200位置
	aStream.Seek(0, SeekOrigin.End);		// 移到文件尾
	aStream.Seek(-20, SeekOrigin.Current);	// 从当前位置反向移动20

2.FileStream类

FileStream是从Stream 中直接派生而来的。FileStream对象既可以从文件中读出内容,也可以向文件中写人内容,并且可以处理字节、字符、字符串以及其他一些数据类型。该对象也可以被用来执行标准的输入/输出及标准错误的输出。
应该注意FileStream对象通常不单独使用,因为其应用比较接近于底层。该对象只能对字节进行读写操作,因此在使用时必须把字符串、数字以及对象都转换成字节才能将其传递到FileStream中。鉴于此,FileStream通常被包装到其他一些类中加以使用,如BinaryWriter或者TextReader,这些类可以处理高层的数据结构。FileStream类具有很多形式的构造方法,因而可以根据以下这些参数的不同组合采用不同的FileStream构造方法:

  • ① 文件名;
  • ② 文件句柄——用来表示文件句柄的一个整数;
  • ③ 访问模式——FileMode 枚举值之一;
  • ④ 读/写权限——FileAccess 枚举值之一;
  • ⑤ 共享模式——FileShare 枚举值之一;
  • ⑥ 缓冲器大小。

表6-3、表6-4和表6-5 分别说明了文件的访问模式、访问权限以及共享模式。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
例如,要创建一个文件,并使该文件支持共享的读操作:

	FileStream fs = new FileStream(@"c:\temp\foo.txt", FileMode.Create, FileAccess.Reead);

FIleStream可以通过同步或者异步方式创建,同时,除了从Stream中继承的属性之外,还增加IsAsync属性。并且,该类中还增加下表列出的方法:
在这里插入图片描述
GetHandle()方法能够返回一个可以用于本地操作系统函数(如Win32中的ReadFile())的标识符,但该方法一定要慎用。如果使用文件句柄对基础文件做了某些改动,然后又试图在该文件上使用FileStream,则有可能会破坏文件中的数据。
FileStream类在操作时,可能会产生以下几种异常:

  • ① ArgumentException——路径为空字符
  • ② ArgumentNullException——路径是一个null引用
  • ③ SecurityException——对文件没有操作权限
  • ④ FileNotFoundException——找不到文件
  • ⑤ IOException——发生了一些其他的I/O错误,如指定了一个错误的驱动符
  • ⑥ DirectoryNotFoundException——目录不存在

例,通过FileStream来读写文件的内容:

using System;
using System.IO;
class Test
{
    
    
	static void Main(){
    
    
		try {
    
    
		FileStream fsw = new FileStream("test.dat", FileMode.Create, FileAccess.Write);

			// Wirte some data to the stream
			fsw.WriteByte(33);
			fsw.Write(new byte[] {
    
     34, 35, 36 }, 0, 3);
			fsw.Close();

			FileStream fsr = new FileStream("test.dat", FileMode.Open, FileAccess.Read);
			Console.WriteLine(fsr.ReadByte());
			Console.WriteLine(fsr.ReadByte());
			Console.WriteLine(fsr.ReadByte());
			Console.WriteLine(fsr.ReadByte());
		} catch (Exception e) {
    
    
			Console.WriteLine("Exception:" + e.ToString());
		}
	}
}

运行结果:
在这里插入图片描述

3.MemoryStream类

MemoryStream类也是从Stream中直接继承而来的,它使用内存代替文件来存储流,但其处理与FileStream非常类似。MemoryStream把数据以字节数组的形式存储在内存中,并且可以用来代替应用程序中临时文件的作用。
如同FileStream一样,MemoryStream也有很多构造方法。其中以下两种比较常用:

	MemoryStream();
	MemoryStream(byte []);

用这里的第一个构造方法建立MemoryStream,当向流的末尾写入数据时,MemoryStream可以随之扩张。用第二个构造方法所建立的是基于指定字节数组的MemoryStream类的新实例,这样建立的流无法调整大小。
除了从Stream继承的属性外,MemoryStream还增加了一个Capacity属性。Capacity属性可以用来指出当前分配到流上的字节数。当使用基于字节数组的流时,这一属性是非常有用的,因为该属性可以告知数组的大小,而Length属性则可以指出当前正被使用的字节数。
MemoryStream不能够执行异步的读/写方法,因为对内存的I/O不需要这种特性。但该对象可以执行下面3种附加方法。

  • GetBuffer()——返回对流中的字节数组的一个引用
  • ToArray()——将所有内容写入到字节数组中
  • WriteTo()——将流中的内容写入到另一个Stream中

例,使用MemoryStream对内存进行操作:

using System;
using System.IO;
class Test
{
    
    
	static void Main(){
    
    
		try {
    
    
			byte[] ary = {
    
     33, 34, 35, 36, 37 };
			int b;

			MemoryStream msr = new MemoryStream(ary);
			MemoryStream msw = new MemoryStream();

			while ((b = msr.ReadByte()) != -1) {
    
    
				msw.WriteByte((byte)(b + 3));
			}
			byte[] result = msw.ToArray();

			foreach (byte bt in result)
				Console.WriteLine(bt);

		} catch (Exception e) {
    
    
			Console.WriteLine("Exception:" + e.ToString());
		}
	}
}

运行结果:
在这里插入图片描述

4.BufferedStrea类

BufferedStream可以提高读写操作的执行效率,因为该类可以把数据缓存到内存中,从而减少了对操作系统的调用次数。BufferedStream不能够单独使用,而应该将其包装到流的其他一些类型中,特别是下面所描述的BinaryWriterBinaryReader类型的流中。另外,将网络流(NetworkStream)进行缓存包装也是常见的。对于BinaryStream调用Flush()操作可以让缓存的内容真正地写到流中。

猜你喜欢

转载自blog.csdn.net/qq_45349225/article/details/114373805