🐋作者简介:博主是一位.Net开发者,同时也是RPA和低代码平台的践行者。
🐬个人主页: 会敲键盘的肘子
🐰系列专栏: .Net实用方法总结
🦀专栏简介:博主针对.Net开发和C站问答过程中遇到的问题进行总结,形成本专栏,希望可以帮助到您解决问题。
🐶座右铭:总有一天你所坚持的会反过来拥抱你。
🌈写在前面:本文主要介绍System.IO命名空间的Stream 类,介绍其常用的方法和示例说明。
👉本文关键字:System.IO、Stream类、流、方法示例、C#
[TOC]
1️⃣ System.IO命名空间
.NET中的IO操作命名空间,包含允许读写文件和数据流的类型以及提供基本文件和目录支持的类型。
我们在.NET中的IO操作,经常需要调用一下几个类。
- FileStream类
文件流类,负责大文件的拷贝,读写。
- Path类
Path类中方法,基本都是对字符串(文件名)的操作,与实际文件没多大关系。
- File类
File类可以进行一些对小文件拷贝、剪切操作,还能读一些文档文件。
- Dirctory类
目录操作,创建文件、删除目录,获取目录下文件名等等。
2️⃣ Stream类
♈ 定义
提供字节序列的一般视图。 这是一个抽象类。
public abstract class Stream : MarshalByRefObject, IAsyncDisposable, IDisposable
♉ 构造函数
初始化 Stream 类的新实例
protected Stream ();
♊ 属性
CanRead 当在派生类中重写时,获取指示当前流是否支持读取的值
public abstract bool CanRead { get; }
示例
using System;
using System.IO;
class TestRW
{
public static void Main(String[] args)
{
FileStream fs = new FileStream("MyFile.txt", FileMode.OpenOrCreate, FileAccess.Read);
if (fs.CanRead && fs.CanWrite)
{
Console.WriteLine("MyFile.txt can be both written to and read from.");
}
else if (fs.CanRead)
{
Console.WriteLine("MyFile.txt is not writable.");
}
}
}
CanWrite 当在派生类中重写时,获取指示当前流是否支持写入功能的值
public abstract bool CanWrite { get; }
示例
using System;
using System.IO;
class TestRW
{
public static void Main(String[] args)
{
FileStream fs = new FileStream("MyFile.txt", FileMode.OpenOrCreate,
FileAccess.Write);
if (fs.CanRead && fs.CanWrite) {
Console.WriteLine("MyFile.txt can be both written to and read from.");
}
else if (fs.CanWrite) {
Console.WriteLine("MyFile.txt is writable.");
}
}
}
//This code outputs "MyFile.txt is writable."
//To get the output message "MyFile.txt can be both written to and read from.",
//change the FileAccess parameter to ReadWrite in the FileStream constructor.
Length 当在派生类中重写时,获取流长度(以字节为单位)
public abstract long Length { get; }
♌ 常用方法
Close() 关闭当前流并释放与之关联的所有资源(如套接字和文件句柄)
public virtual void Close ();
注意:此方法调用 Dispose ,指定true
以释放所有资源。 不需要专门调用 Close 方法。 请确保 Stream 已正确释放每个对象。 可以 Streamusing
Using
在 Visual Basic) 中 (或块中声明对象,以确保释放流及其所有资源,或者可以显式调用 Dispose 方法。
CopyTo(Stream) 从当前流中读取字节并将其写入到另一流中
public void CopyTo (System.IO.Stream destination);
参数
destination
Stream
当前流的内容将复制到的流。
示例
下面的示例将的内容复制 FileStream 到 MemoryStream 中。
// Create the streams.
MemoryStream destination = new MemoryStream();
using (FileStream source = File.Open(@"c:\temp\data.dat",
FileMode.Open))
{
Console.WriteLine("Source length: {0}", source.Length.ToString());
// Copy source to destination.
source.CopyTo(destination);
}
Console.WriteLine("Destination length: {0}", destination.Length.ToString());
CopyTo(Stream, Int32) 使用指定的缓冲区大小,从当前流中读取字节并将其写入到另一流中
public virtual void CopyTo (System.IO.Stream destination, int bufferSize);
参数
destination
Stream
当前流的内容将复制到的流。
bufferSize
Int
缓冲区的大小。 此值必须大于零。 默认大小为 81920。
CopyToAsync(Stream) 从当前流中异步读取字节并将其写入到另一个流中
public System.Threading.Tasks.Task CopyToAsync (System.IO.Stream destination);
参数
destination
Stream
当前流的内容将复制到的流。
示例
下面的示例演示如何使用两个 FileStream 对象将文件从一个目录异步复制到另一个目录。 FileStream 类是从 Stream 类派生的。 请注意, Click 控件的事件处理程序 Button 使用修饰符标记, async
因为它调用异步方法
using System;
using System.Threading.Tasks;
using System.Windows;
using System.IO;
namespace WpfApplication
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
string StartDirectory = @"c:\Users\exampleuser\start";
string EndDirectory = @"c:\Users\exampleuser\end";
foreach (string filename in Directory.EnumerateFiles(StartDirectory))
{
using (FileStream SourceStream = File.Open(filename, FileMode.Open))
{
using (FileStream DestinationStream = File.Create(EndDirectory + filename.Substring(filename.LastIndexOf('\\'))))
{
await SourceStream.CopyToAsync(DestinationStream);
}
}
}
}
}
}
CopyToAsync方法使你可以在不阻塞主线程的情况下执行占用大量资源的 i/o 操作。
Dispose() 释放由 Stream 使用的所有资源
public void Dispose ();
Read(Byte[], Int32, Int32) 当在派生类中重写时,从当前流读取字节序列,并将此流中的位置提升读取的字节数
public abstract int Read (byte[] buffer, int offset, int count);
参数
buffer
Byte[]
字节数组。 当此方法返回时,此缓冲区包含指定的字符数组,此数组中
offset
和 (offset
+count
- 1) 之间的值被从当前源中读取的字节所替换。
offset
buffer
中的从零开始的字节偏移量,从此处开始存储从当前流中读取的数据。
count
要从当前流中最多读取的字节数。
返回
Int32读入缓冲区中的总字节数。 如果很多字节当前不可用,则总字节数可能小于请求的字节数;如果已到达流结尾,则为零 (0)。
示例
下面的示例演示如何使用 Read 读取数据块。
using System;
using System.IO;
public class Block
{
public static void Main()
{
Stream s = new MemoryStream();
for (int i = 0; i < 122; i++)
{
s.WriteByte((byte)i);
}
s.Position = 0;
// Now read s into a byte buffer with a little padding.
byte[] bytes = new byte[s.Length + 10];
int numBytesToRead = (int)s.Length;
int numBytesRead = 0;
do
{
// Read may return anything from 0 to 10.
int n = s.Read(bytes, numBytesRead, 10);
numBytesRead += n;
numBytesToRead -= n;
} while (numBytesToRead > 0);
s.Close();
Console.WriteLine("number of bytes read: {0:d}", numBytesRead);
}
}
使用 CanRead 属性确定当前实例是否支持读取。 使用 ReadAsync 方法从当前流异步读取。此方法的实现从当前流中读取最大字节
count
数,并存储从buffer
开始的字节offset
。 流中的当前位置按读取的字节数进行高级;但是,如果发生异常,则流中的当前位置保持不变。 实现返回读取的字节数。 在没有任何数据可用时,实现将一直阻止,直到至少可以读取一个字节的数据。 Read 仅在流中没有更多数据且预期没有更多数据(例如关闭套接字或文件结尾) (返回 0) 。 即使尚未到达流的末尾,实现也能够返回比请求的更少的字节。
ReadAsync(Byte[], Int32, Int32) 从当前流异步读取字节序列,并将流中的位置提升读取的字节数
public System.Threading.Tasks.Task<int> ReadAsync (byte[] buffer, int offset, int count);
参数
buffer
Byte[]
要写入数据的缓冲区。
offset
buffer
中的字节偏移量,从该偏移量开始写入从流中读取的数据。
count
最多读取的字节数。
返回
Task< Int32>表示异步读取操作的任务。
TResult
参数的值包含读入缓冲区的总字节数。 如果当前可用字节数少于所请求的字节数,则该结果值可小于所请求的字节数;如果已到达流结尾时,则为 0(零)。
示例
下面的示例演示如何以异步方式从文件读取。 该示例使用 FileStream 类,该类派生自 Stream 类。
using System;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.IO;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
string filename = @"c:\Temp\userinputlog.txt";
byte[] result;
using (FileStream SourceStream = File.Open(filename, FileMode.Open))
{
result = new byte[SourceStream.Length];
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
}
UserInput.Text = System.Text.Encoding.ASCII.GetString(result);
}
}
}
Write(Byte[], Int32, Int32) 当在派生类中重写时,向当前流中写入字节序列,并将此流中的当前位置提升写入的字节数
public abstract void Write (byte[] buffer, int offset, int count);
参数
buffer
Byte[]
字节数组。 此方法将
count
个字节从buffer
复制到当前流。
offset
buffer
中的从零开始的字节偏移量,从此处开始将字节复制到当前流。
count
要写入当前流的字节数。
使用 CanWrite 属性确定当前实例是否支持写入。 使用 WriteAsync 方法异步写入当前流。
如果写入操作成功,则流中的位置将按写入的字节数前进。 如果发生异常,则流中的位置保持不变。
WriteAsync(Byte[], Int32, Int32) 将字节序列异步写入当前流,并将流的当前位置提升写入的字节数
public System.Threading.Tasks.Task WriteAsync (byte[] buffer, int offset, int count);
参数
buffer
Byte[]
从中写入数据的缓冲区。
offset
buffer
中的从零开始的字节偏移量,从此处开始将字节复制到该流。
count
最多写入的字节数。
返回
Task表示异步写入操作的任务。
示例
下面的示例演示如何异步写入文件。 该示例使用 FileStream 类,该类派生自 Stream 类。
using System;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.IO;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
UnicodeEncoding uniencoding = new UnicodeEncoding();
string filename = @"c:\Users\exampleuser\Documents\userinputlog.txt";
byte[] result = uniencoding.GetBytes(UserInput.Text);
using (FileStream SourceStream = File.Open(filename, FileMode.OpenOrCreate))
{
SourceStream.Seek(0, SeekOrigin.End);
await SourceStream.WriteAsync(result, 0, result.Length);
}
}
}
}
♍ 注解
Stream 所有流的抽象基类。 流是字节序列的抽象,例如文件、输入/输出设备、进程中通信管道或 TCP/IP 套接字。 Stream类及其派生类提供这些不同类型的输入和输出的一般视图,并将程序员与操作系统和基础设备的具体详细信息隔离开来。
流涉及三个基本操作:
- 可以从流中读取。 读取是将数据从流传输到数据结构(如字节数组)中。
- 可以写入流。 写入是指将数据从数据结构传输到流中。
- 流可以支持查找。 查找是指查询和修改流中的当前位置。 查找功能取决于流具有的后备存储的类型。 例如,网络流没有当前位置的统一概念,因此通常不支持查找。
继承自的一些常用流 Stream 是 FileStream 、和 MemoryStream 。
此类型实现 IDisposable 接口。 在使用完类型后,您应直接或间接释放类型。 若要直接释放类型,请在 try
/catch
块中调用其 Dispose 方法。 若要间接释放类型,请使用 using
(在 C# 中)或 Using
(在 Visual Basic 中)等语言构造。 有关详细信息,请参阅 IDisposable 接口主题中的“使用实现 IDisposable 的对象”一节。
释放 Stream 对象将刷新所有缓冲数据,并在本质上调用 Flush 方法。 Dispose 还会释放操作系统资源(如文件句柄、网络连接或用于任何内部缓冲的内存)。 BufferedStream类提供了围绕另一个流包装缓冲流以提高读写性能的功能。
从 .NET Framework 4.5 开始, Stream 类包括异步方法来简化异步操作。 异步方法 Async
在其名称中包含,例如 ReadAsync 、、 WriteAsync CopyToAsync 和 FlushAsync 。 利用这些方法,您可以在不阻塞主线程的情况下执行占用大量资源的 i/o 操作。 在 Windows 8.x 应用商店应用或桌面应用中一个耗时的流操作可能阻塞 UI 线程并让应用看起来好像不工作时,这种性能的考虑就显得尤为重要了。 异步方法与 async
await
Visual Basic 和 c # 中的和关键字结合使用。
♎ 更多方法
更多方法请查阅官方文档 Stream类。
⭐写在结尾:文章中出现的任何错误请大家批评指出,一定及时修改。
希望写在这里的小伙伴能给个三连支持!