39、.NET 中的流操作:从基础到高级应用

.NET 中的流操作:从基础到高级应用

在 .NET 编程中,流(Stream)是处理输入输出(I/O)操作的核心概念。流提供了一种统一的方式来处理不同类型的数据源,包括文件、内存和网络连接。本文将深入探讨 .NET 中流的各种应用场景,包括二进制文件读写、缓冲流、文本文件处理、异步 I/O 和网络 I/O。

二进制文件读写

当不确定文件是否仅包含文本时,将其视为二进制文件进行处理是最安全的做法。在 .NET 中,可以使用 Stream 类来执行二进制文件的读写操作。 Stream 类提供了多个重要方法,如 Read() Write() BeginRead() BeginWrite() Flush()

以下是一个简单的二进制文件读写示例:

using System;
using System.IO;
namespace ImplementingBinaryReadWriteToFile
{
    class Tester
    {
        const int SizeBuff = 1024;
        public static void Main()
        {
            Tester t = new Tester();
            t.Run();
        }
        private void Run()
        {
            Stream inputStream = File.OpenRead(@"C:\test\source\test1.cs");
            Stream outputStream = File.OpenWrite(@"C:\test\source\test1.bak");
            byte[] buffer = new Byte[SizeBuff];
            int bytesRead;
            while ((bytesRead = inputStream.Read(buffer, 0, SizeBuff)) > 0)
            {
                outputStream.Write(buffer, 0, bytesRead);
            }
            inputStream.Close();
            outputStream.Close();
        }
    }
}

在这个示例中,我们首先创建了两个 Stream 对象,一个用于读取文件,另一个用于写入文件。然后,我们使用一个循环不断从输入流中读取数据,并将其写入输出流,直到没有更多数据可读为止。最后,我们关闭了两个流。

缓冲流

在前面的示例中,我们手动创建了一个缓冲区来读取数据。然而,操作系统可能可以通过一次读取更多或更少的字节来提高效率。缓冲流( BufferedStream )可以帮助我们实现这一点。

以下是一个使用缓冲流的示例:

using System;
using System.IO;
namespace Programming_CSharp
{
    class Tester
    {
        const int SizeBuff = 1024;
        public static void Main()
        {
            Tester t = new Tester();
            t.Run();
        }
        private void Run()
        {
            Stream inputStream = File.OpenRead(@"C:\test\source\folder3.cs");
            Stream outputStream = File.OpenWrite(@"C:\test\source\folder3.bak");
            BufferedStream bufferedInput = new BufferedStream(inputStream);
            BufferedStream bufferedOutput = new BufferedStream(outputStream);
            byte[] buffer = new Byte[SizeBuff];
            int bytesRead;
            while ((bytesRead = bufferedInput.Read(buffer, 0, SizeBuff)) > 0)
            {
                bufferedOutput.Write(buffer, 0, bytesRead);
            }
            bufferedOutput.Flush();
            bufferedInput.Close();
            bufferedOutput.Close();
        }
    }
}

在这个示例中,我们首先创建了两个普通的 Stream 对象,然后将它们传递给 BufferedStream 的构造函数,创建了两个缓冲流。接下来,我们使用缓冲流进行读写操作,最后记得调用 Flush() 方法确保数据被写入文件。

文本文件处理

如果确定要读写的文件仅包含文本,可以使用 StreamReader StreamWriter 类来简化文本处理。这两个类提供了 ReadLine() WriteLine() 方法,方便逐行读写文本。

以下是一个文本文件读写的示例:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace ReadingWritingToTextFile
{
    class Tester
    {
        public static void Main()
        {
            Tester t = new Tester();
            t.Run();
        }
        private void Run()
        {
            FileInfo theSourceFile = new FileInfo(@"C:\test\source\test.cs");
            StreamReader reader = theSourceFile.OpenText();
            StreamWriter writer = new StreamWriter(@"C:\test\source\test.bak", false);
            string text;
            do
            {
                text = reader.ReadLine();
                writer.WriteLine(text);
                Console.WriteLine(text);
            } while (text != null);
            reader.Close();
            writer.Close();
        }
    }
}

在这个示例中,我们首先创建了一个 FileInfo 对象,然后调用其 OpenText() 方法创建了一个 StreamReader 对象。接着,我们创建了一个 StreamWriter 对象,并使用一个循环逐行读取文件内容,将其写入新文件并打印到控制台。最后,我们关闭了两个流。

异步 I/O

前面介绍的所有程序都是同步 I/O,即在读写操作进行时,程序的其他活动会被暂停。对于大文件或网络读写,异步 I/O 可以提高程序的性能。.NET 框架通过 Stream 类的 BeginRead() BeginWrite() 方法提供了异步 I/O 支持。

以下是一个异步 I/O 的示例:

using System;
using System.IO;
namespace AsynchronousIO
{
    public class AsynchIOTester
    {
        private Stream inputStream;
        private AsyncCallback myCallBack;
        private byte[] buffer;
        const int BufferSize = 256;
        AsynchIOTester()
        {
            inputStream = File.OpenRead(@"C:\test\source\AskTim.txt");
            buffer = new byte[BufferSize];
            myCallBack = new AsyncCallback(this.OnCompletedRead);
        }
        public static void Main()
        {
            AsynchIOTester theApp = new AsynchIOTester();
            theApp.Run();
        }
        void Run()
        {
            inputStream.BeginRead(
                buffer, 0, buffer.Length,
                myCallBack, null);
            for (long i = 0; i < 500000; i++)
            {
                if (i % 1000 == 0)
                {
                    Console.WriteLine("i: {0}", i);
                }
            }
        }
        void OnCompletedRead(IAsyncResult asyncResult)
        {
            int bytesRead = inputStream.EndRead(asyncResult);
            if (bytesRead > 0)
            {
                String s = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                Console.WriteLine(s);
                inputStream.BeginRead(
                    buffer, 0, buffer.Length, myCallBack, null);
            }
        }
    }
}

在这个示例中,我们首先创建了一个 AsynchIOTester 类,在构造函数中打开文件、分配缓冲区并设置回调方法。在 Run() 方法中,我们调用 BeginRead() 方法开始异步读取文件,然后进行其他工作。当读取完成时,CLR 会调用回调方法 OnCompletedRead() ,在该方法中我们处理读取的数据,并可以再次调用 BeginRead() 方法进行下一次读取。

网络 I/O

网络 I/O 与本地文件读写类似,可以使用基于套接字(Socket)创建的流来实现。套接字在客户端/服务器和点对点(P2P)应用程序以及远程过程调用中非常有用。

在本节中,我们将创建一个基于 TCP/IP 的服务器和客户端连接。TCP/IP 是一种基于连接的流协议,一旦建立连接,两个进程就可以像通过直接电话线连接一样进行通信。

以下是网络 I/O 的基本流程:
1. 端口概念 :每个应用程序在网络中需要一个唯一的 ID,即端口。端口号范围从 0 到 65535,分为不同的范围:
- 0 - 1023:知名端口
- 1024 - 49151:注册端口
- 49152 - 65535:动态和/或私有端口
2. 服务器端操作
- 实例化 TcpListener ,指定 IP 地址和端口。
- 调用 Start() 方法开始接受网络连接。
- 调用 AcceptSocket() 方法等待客户端连接,该方法会阻塞当前线程。
3. 客户端操作
- 连接到指定的 IP 地址和端口。

以下是一个简单的 TCP/IP 服务器示例的 mermaid 流程图:

graph TD;
    A[创建 TcpListener] --> B[调用 Start() 方法];
    B --> C[调用 AcceptSocket() 方法];
    C --> D[等待客户端连接];
    D --> E[与客户端通信];

通过以上介绍,我们了解了 .NET 中流的各种应用场景,包括二进制文件读写、缓冲流、文本文件处理、异步 I/O 和网络 I/O。掌握这些知识可以帮助我们更高效地处理各种 I/O 操作,提高程序的性能和稳定性。

.NET 中的流操作:从基础到高级应用(续)

网络 I/O 深入探讨

在前面我们已经了解了网络 I/O 的基本概念和 TCP/IP 连接的基本流程,接下来我们进一步深入探讨网络 I/O 的一些细节。

在实际应用中,服务器可能需要同时处理多个客户端的请求。如果采用前面简单的单线程模型,当一个客户端连接时,其他客户端就需要等待,这显然不能满足高并发的需求。为了解决这个问题,我们可以使用多线程来处理客户端连接。

以下是一个简单的多线程 TCP/IP 服务器示例:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

class Server
{
    private const int Port = 12345;

    public static void Main()
    {
        TcpListener listener = new TcpListener(IPAddress.Any, Port);
        listener.Start();
        Console.WriteLine("Server started. Waiting for connections...");

        while (true)
        {
            TcpClient client = listener.AcceptTcpClient();
            Console.WriteLine("Client connected.");

            // 为每个客户端创建一个新线程来处理
            Thread clientThread = new Thread(() => HandleClient(client));
            clientThread.Start();
        }
    }

    private static void HandleClient(TcpClient client)
    {
        try
        {
            NetworkStream stream = client.GetStream();
            byte[] buffer = new byte[1024];
            int bytesRead;

            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
            {
                string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                Console.WriteLine("Received: " + message);

                // 回显消息给客户端
                byte[] response = Encoding.ASCII.GetBytes("Server received: " + message);
                stream.Write(response, 0, response.Length);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error handling client: " + ex.Message);
        }
        finally
        {
            client.Close();
            Console.WriteLine("Client disconnected.");
        }
    }
}

这个示例中,服务器在一个无限循环中等待客户端连接。当有客户端连接时,会为该客户端创建一个新的线程来处理,这样就可以同时处理多个客户端的请求。

客户端代码示例如下:

using System;
using System.Net.Sockets;
using System.Text;

class Client
{
    private const string ServerIp = "127.0.0.1";
    private const int Port = 12345;

    public static void Main()
    {
        try
        {
            TcpClient client = new TcpClient();
            client.Connect(ServerIp, Port);
            Console.WriteLine("Connected to server.");

            NetworkStream stream = client.GetStream();
            string message = "Hello, server!";
            byte[] data = Encoding.ASCII.GetBytes(message);
            stream.Write(data, 0, data.Length);

            byte[] buffer = new byte[1024];
            int bytesRead = stream.Read(buffer, 0, buffer.Length);
            string response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Server response: " + response);

            client.Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }
    }
}

客户端代码相对简单,它连接到服务器,发送一条消息,然后接收服务器的响应。

不同流类型的比较

在 .NET 中,有多种不同类型的流,它们适用于不同的场景。以下是一个简单的比较表格:
| 流类型 | 特点 | 适用场景 |
| ---- | ---- | ---- |
| FileStream | 用于读写文件,支持同步和异步操作 | 本地文件的读写 |
| MemoryStream | 数据直接存储在内存中,无需与外部设备交互 | 临时数据的处理,如数据缓存 |
| NetworkStream | 基于网络连接的流,用于网络 I/O | 网络通信,如客户端/服务器应用 |
| BufferedStream | 提供内部缓冲区,提高读写效率 | 频繁读写操作,尤其是对磁盘文件的读写 |
| StreamReader StreamWriter | 专门用于文本处理,提供逐行读写功能 | 文本文件的读写 |

流操作的最佳实践

在进行流操作时,有一些最佳实践可以帮助我们避免常见的错误,提高代码的性能和可维护性。

  1. 资源管理 :所有的流对象都需要正确地释放资源,避免资源泄漏。可以使用 using 语句来确保流对象在使用完毕后自动关闭和释放资源。例如:
using (Stream inputStream = File.OpenRead(@"C:\test\source\test1.cs"))
using (Stream outputStream = File.OpenWrite(@"C:\test\source\test1.bak"))
{
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
    {
        outputStream.Write(buffer, 0, bytesRead);
    }
}
  1. 异常处理 :流操作可能会抛出各种异常,如文件不存在、网络连接失败等。在代码中应该进行适当的异常处理,确保程序的健壮性。例如:
try
{
    Stream inputStream = File.OpenRead(@"C:\test\source\test1.cs");
    // 其他操作
}
catch (FileNotFoundException ex)
{
    Console.WriteLine("文件未找到: " + ex.Message);
}
catch (IOException ex)
{
    Console.WriteLine("I/O 错误: " + ex.Message);
}
  1. 性能优化 :对于频繁的读写操作,可以使用缓冲流来提高性能。同时,合理选择缓冲区的大小也很重要,过大或过小的缓冲区都可能影响性能。
总结

通过本文的介绍,我们全面了解了 .NET 中流的各种应用场景,包括二进制文件读写、缓冲流、文本文件处理、异步 I/O 和网络 I/O。我们学习了不同流类型的特点和适用场景,以及流操作的最佳实践。掌握这些知识可以帮助我们更高效地处理各种 I/O 操作,提高程序的性能和稳定性。在实际开发中,我们应该根据具体的需求选择合适的流类型和操作方式,同时注意资源管理和异常处理,确保代码的健壮性和可维护性。

希望本文对你在 .NET 流操作方面的学习和实践有所帮助。如果你有任何疑问或建议,欢迎留言讨论。

以下是一个总结流操作流程的 mermaid 流程图:

graph LR;
    A[选择流类型] --> B{是否为文件操作};
    B -- 是 --> C[使用 FileStream 或 StreamReader/StreamWriter];
    B -- 否 --> D{是否为网络操作};
    D -- 是 --> E[使用 NetworkStream];
    D -- 否 --> F{是否需要临时缓存};
    F -- 是 --> G[使用 MemoryStream];
    F -- 否 --> H[根据需求选择其他流];
    C --> I[进行读写操作];
    E --> I;
    G --> I;
    H --> I;
    I --> J[资源管理和异常处理];

通过这个流程图,我们可以清晰地看到在不同场景下如何选择合适的流类型,并进行相应的操作。

【SCI复现】基于纳什博弈的多微网主体电热双层共享策略研究(Matlab代码实现)内容概要:本文围绕“基于纳什博弈的多微网主体电热双层共享策略研究”展开,结合Matlab代码实现,复现了SCI级别的科研成果。研究聚焦于多个微网主体之间的能源共享问题,引入纳什博弈理论构建双层优化模型,上层为各微网间的非合作博弈策略,下层为各微网内部电热联合优化调度,实现能源高效利用与经济性目标的平衡。文中详细阐述了模型构建、博弈均衡求解、约束处理及算法实现过程,并通过Matlab编程进行仿真验证,展示了多微网在电热耦合条件下的运行特性和共享效益。; 适合人群:具备一定电力系统、优化理论和博弈论基础知识的研究生、科研人员及从事能源互联网、微电网优化等相关领域的工程师。; 使用场景及目标:① 学习如何将纳什博弈应用于多主体能源系统优化;② 掌握双层优化模型的建模与求解方法;③ 复现SCI论文中的仿真案例,提升科研实践能力;④ 为微电网集群协同调度、能源共享机制设计提供技术参考。; 阅读建议:建议读者结合Matlab代码逐行理解模型实现细节,重点关注博弈均衡的求解过程与双层结构的迭代逻辑,同时可尝试修改参数或扩展模型以适应不同应用场景,深化对多主体协同优化机制的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值