能说说流的概念吗?.NET中有哪些流?

流是一种针对字节流的操作,它类似于内存与文件之间的一个管道。在对一个文件进行处理时,本质上需要经过借助OS提供的API来进行打开文件,读取文件中的字节流,再关闭文件等操作,其中读取文件的过程就可以看作是字节流的一个过程。.

能说说流的概念吗?.NET中有哪些流?

常见的流类型包括:文件流、终端操作流以及网络Socket等,在.NET中,System.IO.Stream类型被设计为作为所有流类型的虚基类,所有的常见流类型都继承自System.IO.Stream类型,当我们需要自定义一种流类型时,也应该直接或者间接地继承自Stream类型。下图展示了在.NET中常见的流类型以及它们的类型结构:

能说说流的概念吗?.NET中有哪些流?

从上图中可以发现,Stream类型继承自MarshalByRefObject类型,这保证了流类型可以跨越应用程序域进行交互。所有常用的流类型都继承自System.IO.Stream类型,这保证了流类型的同一性,并且屏蔽了底层的一些复杂操作,使用起来非常方便

下面的代码中展示了如何在.NET中使用FileStream文件流进行简单的文件读写操作:

public class Program
{
    private const int bufferlength = 1024;

    public static void Main(string[] args)
    {
        //创建一个文件,并写入内容
        string filename = @"C:\TestStream.txt";
        string filecontent = GetTestString();

        try
        {
            if (File.Exists(filename))
            {
                File.Delete(filename);
            }

            // 创建文件并写入内容
            using (FileStream fs = new FileStream(filename, FileMode.Create))
            {
                Byte[] bytes = Encoding.UTF8.GetBytes(filecontent);
                fs.Write(bytes, 0, bytes.Length);
            }

            // 读取文件并打印出来
            using (FileStream fs = new FileStream(filename, FileMode.Open))
            {
                Byte[] bytes = new Byte[bufferlength];
                UTF8Encoding encoding = new UTF8Encoding(true);
                while (fs.Read(bytes, 0, bytes.Length) > 0)
                {
                    Console.WriteLine(encoding.GetString(bytes));
                }
            }
            // 循环分批读取打印
            //using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
            //{
            //    Byte[] bytes = new Byte[bufferlength];
            //    int bytesRead;
            //    do
            //    {
            //        bytesRead = fs.Read(bytes, 0, bufferlength);
            //        Console.WriteLine(Encoding.UTF8.GetString(bytes, 0, bytesRead));
            //    } while (bytesRead > 0);
            //}
        }
        catch (IOException ex)
        {
            Console.WriteLine(ex.Message);
        }

        Console.ReadKey();
    }

    // 01.取得测试数据
    static string GetTestString()
    {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 10; i++)
        {
            builder.Append("我是测试数据\r\n");
            builder.Append("我是长江" + (i + 1) + "号\r\n");
        }
        return builder.ToString();
    }
}

上述代码的执行结果如下图所示:

能说说流的概念吗?.NET中有哪些流?

在实际开发中,我们经常会遇到需要传递一个比较大的文件,或者事先无法得知文件大小(Length属性抛出异常),因此也就不能创建一个尺寸正好合适的Byte[]数组,此时只能分批读取和写入,每次只读取部分字节,直到文件尾。例如我们需要复制G盘中一个大小为4.4MB的mp3文件到C盘中去,假设我们对大小超过2MB的文件都采用分批读取写入机制,可以通过如下代码实现:

public class Program
{
    private const int BufferSize = 10240; // 10 KB
    public static void Main(string[] args)
    {
        string fileName = @"G:\My Musics\BlueMoves.mp3"; // Source 4.4 MB
        string copyName = @"C:\BlueMoves-Copy.mp3"; // Destination 4.4 MB
        using (Stream source = new FileStream(fileName, FileMode.Open, FileAccess.Read))
        {
            using (Stream target = new FileStream(copyName, FileMode.Create, FileAccess.Write))
            {
                byte[] buffer = new byte[BufferSize];
                int bytesRead;
                do
                {
                    // 从源文件中读取指定的10K长度到缓存中
                    bytesRead = source.Read(buffer, 0, BufferSize);
                    // 从缓存中写入已读取到的长度到目标文件中
                    target.Write(buffer, 0, bytesRead);
                } while (bytesRead > 0);
            }
        }
        Console.ReadKey();
    }
}

上述代码中,设置了缓存buffer大小为10K,即每次只读取10K的内容长度到buffer中,通过循环的多次读写和写入完成整个复制操作。