MemoryStream类学习


1:MemoryStream基本方法及使用

微软:(https://docs.microsoft.com/zh-cn/dotnet/api/system.io.memorystream?view=net-6.0)

 static void Main()
        {
            int count;
            byte[] byteArray;
            char[] charArray;
            UnicodeEncoding uniEncoding = new UnicodeEncoding();

            // Create the data to write to the stream.
            byte[] firstString = uniEncoding.GetBytes(
                "Invalid file path characters are: ");
            byte[] secondString = uniEncoding.GetBytes(
                Path.GetInvalidPathChars());

            using (MemoryStream memStream = new MemoryStream(100))
            {
                // Write the first string to the stream.
                memStream.Write(firstString, 0, firstString.Length);

                // Write the second string to the stream, byte by byte.
                count = 0;
                while (count < secondString.Length)
                {
                    memStream.WriteByte(secondString[count++]);
                }

                // Write the stream properties to the console.
                Console.WriteLine(
                    "Capacity = {0}, Length = {1}, Position = {2}\n",
                    memStream.Capacity.ToString(),
                    memStream.Length.ToString(),
                    memStream.Position.ToString());

                // Set the position to the beginning of the stream.
                memStream.Seek(0, SeekOrigin.Begin);

                // Read the first 20 bytes from the stream.
                byteArray = new byte[memStream.Length];
                count = memStream.Read(byteArray, 0, 20);

                // Read the remaining bytes, byte by byte.
                while (count < memStream.Length)
                {
                    byteArray[count++] = (byte)memStream.ReadByte();
                }

                // Decode the byte array into a char array
                // and write it to the console.
                charArray = new char[uniEncoding.GetCharCount(      //注意这一步140变70
                    byteArray, 0, count)];
                uniEncoding.GetDecoder().GetChars(
                    byteArray, 0, count, charArray, 0);
                Console.WriteLine(charArray);
                Console.ReadKey();
            }
        }

注意:【浅析unicode和UTF-8、UTF-16、UTF-32的区别:】

1、区别编码字符集和字符集编码

首先要注意的是unicode是编码字符集,而UTF-8、UTF-16、UTF-32是字符集编码(好绕哎有没有)。下面我来具体解释一下:

比如汉字的”汉”,在unicode中,汉”的unicode值为0x6C49。问:把这个”汉”字保存到计算机中(硬盘、内存),机器码是多少呢?
学过《计算机组成原理》的人都知道,计算机内部存储的形式都是0101的二进制数字串。”汉”字保存在计算机里肯定也是0101的数字串。”汉”的unicode值是0x6C49,转化为2进制 1101100 01001001,那么把这个”汉”字保存到计算机中也是 1101100 01001001 吗?答案:NO!

答案:取决于用到的字符集编码是哪种
比如你用到的字符集编码是UTF-8,那么”汉”字在计算机内部保存的值为0xE6B189,也就是111001101011000110001001,可以看到”汉”字变成了3个字节。UTF-8用1-4个字节来保存unicode编码的字符。
而如果用UTF-16来保存,那么”汉”字仍为仍为0x6C49,也就是 1101100 01001001。UTF-16只能是选两字节或四字节来保存字符
而UTF-32就是把所有的字符都用32bit也就是4个字节来表示。
所以这就是编码字符集和字符集编码的区别。

2、unicode的一段历史
首先要注意unicode并不是用2个字节来表示字符串的。unicode1.0版本,所有的字符都是2个字节。但是现在的unicode编码在一百一十万左右,远远超过了2的16次方,也就是2个字节。所以,现在讲unicode编码是2个字节是错误的!。 

********************************************************************************************************************************************************************************************

注意:此类型可实现 IDisposable 接口,但实际上没有任何要释放的资源。 这意味着无需通过直接调用 Dispose() 或使用语言构造(C# 中的 using 或 Visual Basic 中的 Using)对其进行处理。

如果 MemoryStream 对象被序列化到资源文件中,则实际上将序列化为 UnmanagedMemoryStream 。 此行为可提供更好的性能,并且能够直接获取指向数据的指针,而不必经历 Stream 方法。

2:托管和非托管

此时这里会牵涉到:托管和非托管的区别

托管和非托管是修饰内存的。
托管的意思,你不用直接操作内存,你需要的时候跟我说。我替你申请,然后给你用,你用完可以告诉我,我帮你释放,如果你忙,忘记告诉我了,我也会在定期去帮你释放的。 这就是托管,你打交道的不是直接的内存,而是.net clr。
非托管的意思就是你要自己负责管理内存,这里所说的内存管理。实际上只是堆上的内存管理,栈内存和以前的一样,函数退出则释放,heap上的内存,非托管内存需要自己分配,调用构造函数(c需要,c++里用new替代这部操作了),使用完毕后,需要自己释放这个内存,如果你不小心,吧只想内存的指针弄丢了,就造成内存泄露了,这个内存泄露在你程序退出之前是无法弥补的,所以要小心。(virtualMalloc的情况不在此描述了。因为分配的也不是heap上的内存,属于扩展内存空间,题外话了。)

简单说,托管的意思是托管内存,但多一层必然会慢,这个么,微软早考虑好了,硬件升级,大家掏钱就能解决的问题不是问题。带来的好处是托管内存不会有泄漏的危险。

那么内存流与其他文件流的区别又是啥:

MemoryStream:和文件流不同,MemoryStream类表示的是保存在内存中的数据流,由内存流封装的数据可以在内存中直接访问。内存一般用于暂时缓存数据以降低应用程序对临时缓冲区和临时文件的需要。

FileStream:

用途:主要用于读写磁盘文件。常用于向磁盘存储数据或读取配置文件。

优点:该类维护内部文件指针,当处理大型文件时非常省时,可以定位到正确的位置。

注意:当打开文件或者创建文件时,流指针默认位于文件头,当调用Read/Write后,流文件会自动向后移动相应的字节数。因此在代码中无须设置,每次调用Read和Write时,读取或写入都是尚未处理过的字节。

缺点:类是通过字节形式来读写数据的,需要自己处理编码转换,把字节数据转换为文本。

MemoryStream

用途:内存流类,主要用于操作内存中的数据。比如说网络中传输数据时可以用流的形式,当我们收到这些流数据时就可以声明 MemoryStream 类来存储并且处理它们。

内存流,在System.IO命名空间下,对保存在内存中的字节数组进行操作。由于内存流的容量可以自动增长,因此在数据加密以及对长度不定的数据进行缓存等场合使用。

MemoryStream类用于向内存而不是磁盘读写数据。MemoryStream封装以无符号字节数组形式存储的数据,该数组在创建MemoryStream对象时被初始化,或者该数组可创建为空数组。可在内存中直接访问这些封装的数据。内存流可降低应用程序中对临时缓冲区和临时文件的需要

 public List Clone(List inputList)
        {
            BinaryFormatter Formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
            System.IO.MemoryStream stream = new System.IO.MemoryStream();
            Formatter.Serialize(stream, inputList);
            stream.Position = 0;
            var outList = Formatter.Deserialize(stream) as List;
            stream.Close();
            return outList;
        }

这里是内存流的一个应用,序列化和反序列化实现了一格深度复制,又使用内存流提升速度。

  public List Clone2(List inputList)
        {
            using (Stream objectStream = new MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(objectStream, inputList);
                objectStream.Seek(0, SeekOrigin.Begin);
                return formatter.Deserialize(objectStream) as List;
            }
        }

 

C