欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#?MemoryStream的具體使用

 更新時間:2025年07月31日 10:10:41   作者:鯉籽鯤  
MemoryStream是.NET中用于內(nèi)存數(shù)據(jù)讀寫的流類,速度快且靈活,適用于臨時緩存、二進制處理等場景,下面就來詳細的介紹一下MemoryStream的使用,感興趣的可以了解一下

前言

在.NET開發(fā)中,流(Stream)是一個用于處理輸入和輸出的抽象類,MemoryStream是流的一個具體實現(xiàn),它允許我們在內(nèi)存中讀寫數(shù)據(jù),就像操作文件一樣,而無需涉及磁盤 I/O 操作。尤其適合需要快速讀寫、轉(zhuǎn)換或傳輸數(shù)據(jù)的場景。本文將詳細講解MemoryStream的使用。

一、什么是 MemoryStream?

1. 定義

MemoryStreamSystem.IO 命名空間中的一個類,它允許我們在內(nèi)存中創(chuàng)建可讀寫的流。與文件流或網(wǎng)絡(luò)流不同,MemoryStream的數(shù)據(jù)存儲在內(nèi)存中,它不需要依賴物理文件,因此讀寫速度非???,適合處理臨時數(shù)據(jù)(如網(wǎng)絡(luò)傳輸、臨時緩存、序列化對象等)。但會占用一定的內(nèi)存資源。

??MemoryStream 是 System.IO 命名空間中的一個類,它實現(xiàn)了 Stream 抽象類,提供了一系列用于操作數(shù)據(jù)流的屬性和方法。

2. 繼承關(guān)系

2. 核心特性

  • 內(nèi)存高效:數(shù)據(jù)直接存儲在內(nèi)存中,無需磁盤 I/O,讀寫速度快。
  • 靈活操作:支持讀寫、重置位置、轉(zhuǎn)換為字節(jié)數(shù)組等數(shù)據(jù)處理操作。
  • 輕量級:無需文件句柄,適合小到中等規(guī)模的數(shù)據(jù)。

3. 用途

  • 處理大量數(shù)據(jù),如圖像、音頻和視頻文件等二進制數(shù)據(jù)。
  • 臨時存儲數(shù)據(jù),如網(wǎng)絡(luò)傳輸過程中的數(shù)據(jù)緩沖。
  • 實現(xiàn)自定義數(shù)據(jù)流邏輯,例如加密或壓縮數(shù)據(jù)。

4. 為什么需要 MemoryStream?

在數(shù)據(jù)處理場景中,頻繁的磁盤IO操作(如讀寫文件)會顯著降低程序性能,尤其是面對海量數(shù)據(jù)或高頻讀寫需求時。MemoryStream作為C#中的內(nèi)存流,將數(shù)據(jù)存儲在內(nèi)存而非硬盤中,避免了磁盤IO瓶頸,讀寫速度更快。它適用于網(wǎng)絡(luò)數(shù)據(jù)傳輸、臨時緩存、二進制數(shù)據(jù)處理等場景,是實現(xiàn)高性能代碼的利器!

二、基礎(chǔ)用法

1. 創(chuàng)建 MemoryStream 對象

MemoryStream有多個構(gòu)造函數(shù),可以根據(jù)需要選擇合適的構(gòu)造函數(shù)來初始化MemoryStream

1)無參構(gòu)造函數(shù)

使用無參構(gòu)造函數(shù)可以創(chuàng)建一個空白的 MemoryStream 對象,其初始容量為 0,隨著數(shù)據(jù)寫入自動擴展。

using System.IO;

MemoryStream memoryStream = new MemoryStream();

2)帶參構(gòu)造函數(shù)

使用帶參構(gòu)造函數(shù)可以根據(jù)指定的容量創(chuàng)建 MemoryStream 對象,或者從一個字節(jié)數(shù)組創(chuàng)建。

? 指定初始容量的構(gòu)造函數(shù)

MemoryStream memoryStream = new MemoryStream(1024);

創(chuàng)建一個初始容量為1024字節(jié)的MemoryStream

? 使用字節(jié)數(shù)組初始化的構(gòu)造函數(shù)

byte[] buffer = new byte[] { 72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33 };
MemoryStream ms = new MemoryStream(buffer);

使用現(xiàn)有的字節(jié)數(shù)組初始化MemoryStream。

2. 寫入數(shù)據(jù)

MemoryStream 提供了多種方法來寫入數(shù)據(jù),最常用的是 Write 方法和 WriteByte 方法。

1)寫入字節(jié)數(shù)組

使用 Write 方法可以將一個字節(jié)數(shù)組寫入 MemoryStream。

byte[] data = new byte[] { 72, 101, 108, 108, 111 }; 
memoryStream.Write(data, 0, data.Length);

2)寫入字符串

MemoryStream 寫入一個字符串,需要將字符串轉(zhuǎn)換為字節(jié)數(shù)組。

string text = "Hello, World!";
byte[] data = System.Text.Encoding.UTF8.GetBytes(text);
memoryStream.Write(data, 0, data.Length);

3)使用 WriteByte 方法

WriteByte 方法可以逐字節(jié)寫入數(shù)據(jù)。

string text = "Hello, World!";
byte[] data = System.Text.Encoding.UTF8.GetBytes(text);
foreach (byte b in data)
{
    memoryStream.WriteByte(b);
}

3. 讀取數(shù)據(jù)

MemoryStream 提供了多種方法來讀取數(shù)據(jù),最常用的是 Read 方法和 ReadByte 方法。

1)讀取字節(jié)數(shù)組

MemoryStream 讀取一定數(shù)量的字節(jié)到字節(jié)數(shù)組中。

byte[] buffer = new byte[11];
int bytesRead = memoryStream.Read(buffer, 0, buffer.Length);

2)讀取字符串

MemoryStream 讀取一定數(shù)量的字節(jié),然后將其轉(zhuǎn)換為字符串。

byte[] buffer = new byte[11];
int bytesRead = memoryStream.Read(buffer, 0, buffer.Length);
string text = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead);

3)使用 ReadByte 方法

ReadByte 方法可以逐字節(jié)讀取數(shù)據(jù)。

List<byte> byteList = new List<byte>();
while (memoryStream.Position < memoryStream.Length)
{
    byteList.Add((byte)memoryStream.ReadByte());
}
string text = System.Text.Encoding.UTF8.GetString(byteList.ToArray());

4. 常用屬性

1)Capacity

獲取或設(shè)置分配給MemoryStream 的字節(jié)數(shù)

MemoryStream memoryStream = new MemoryStream(1024);
int capacity = memoryStream.Capacity;	// 輸出:1024

2)Length

獲取MemoryStream中實際使用的數(shù)據(jù)長度。

MemoryStream memoryStream = new MemoryStream(1024);
long length = memoryStream.Length;	// length = 0

3)Position

獲取或設(shè)置MemoryStream的當(dāng)前讀寫位置。

memoryStream.Position = 0; // 定位到流的開頭

4)CanRead、CanWrite、CanSeek

bool canRead = ms.CanRead;
bool canWrite = ms.CanWrite;
bool canSeek = ms.CanSeek;

表示MemoryStream是否支持讀取、寫入和定位操作。對于MemoryStream,這些屬性通常返回true。

5. 常用輔助方法

1)SetLength 設(shè)置長度

使用 SetLength 方法可以設(shè)置 MemoryStream 的長度,如果新長度小于當(dāng)前長度,數(shù)據(jù)將被截斷;如果新長度大于當(dāng)前長度,數(shù)據(jù)將被擴展。

memoryStream.SetLength(50);

2)Seek 設(shè)置當(dāng)前讀寫位置

ms.Seek(0, SeekOrigin.Begin);

移動MemoryStream的當(dāng)前讀寫位置。

3)ToArray 轉(zhuǎn)換為字節(jié)數(shù)組

使用 ToArray 方法可以將 MemoryStream 的內(nèi)容轉(zhuǎn)換為字節(jié)數(shù)組。

byte[] allBytes = memoryStream.ToArray();

4)GetBuffer 獲取底層緩沖區(qū)的字節(jié)數(shù)組

byte[] buffer = memoryStream.GetBuffer();

GetBuffer方法返回底層緩沖區(qū)的完整字節(jié)數(shù)組(包含未使用的空間),ToArray方法返回僅包含有效數(shù)據(jù)的數(shù)組(排除未使用的空間)。

關(guān)于 ToArray 和 GetBuffer 方法的區(qū)別,詳見:C# MemoryStream 中 ToArray 和 GetBuffer 的區(qū)別

6. Position 注意事項

1)寫入數(shù)據(jù)后的 Position 自動前進

// 創(chuàng)建空的 MemoryStream
using (MemoryStream ms = new MemoryStream())
{
    // 寫入字符串
    byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello, MemoryStream!");
    ms.Write(data, 0, data.Length);
    
    // 寫入后的位置自動前進
    Console.WriteLine($"當(dāng)前位置:{ms.Position}"); // 輸出:20(假設(shè) UTF-8 編碼)
}

2)讀取數(shù)據(jù) 必須重置 Position

using (MemoryStream ms = new MemoryStream())
{
    // 寫入數(shù)據(jù)后重置位置到開頭
    ms.Write(data, 0, data.Length);
    ms.Position = 0; // 必須重置位置才能讀取

    // 讀取數(shù)據(jù)
    byte[] buffer = new byte[ms.Length];
    ms.Read(buffer, 0, (int)ms.Length);
    string result = System.Text.Encoding.UTF8.GetString(buffer);
    Console.WriteLine(result); // 輸出:Hello, MemoryStream!
}

Tips:
除了使用 Position 屬性重置位置外,讀寫前還可用Seek()調(diào)整指針位置,如 stream.Seek(0, SeekOrigin.Begin)。

7. 示例代碼

下面是一個完整的示例,演示了如何使用 MemoryStream

public class Program
{
    public static void Main(string[] args)
    {
        // 創(chuàng)建 MemoryStream
        MemoryStream memoryStream = new MemoryStream();

        // 獲取當(dāng)前讀寫的位置
        Console.WriteLine($"MemoryStream Position: {memoryStream.Position}");// 輸出:MemoryStream Position: 0


        string text = "Hello, World!";
        byte[] data = System.Text.Encoding.UTF8.GetBytes(text);
        // 寫入數(shù)據(jù)
        memoryStream.Write(data, 0, data.Length);

        // 轉(zhuǎn)換為字節(jié)數(shù)組
        Console.WriteLine(BitConverter.ToString(memoryStream.ToArray()));   //輸出:48-65-6C-6C-6F-2C-20-57-6F-72-6C-64-21

        // 設(shè)置長度
        memoryStream.SetLength(50);

        // 獲取長度
        Console.WriteLine($"MemoryStream length: {memoryStream.Length}"); // 輸出:MemoryStream length: 50

        // 獲取當(dāng)前讀寫的位置
        Console.WriteLine($"MemoryStream Position: {memoryStream.Position}");// 輸出:MemoryStream Position: 13

        // 讀取數(shù)據(jù)
        byte[] buffer = new byte[5];
        int bytesRead = memoryStream.Read(buffer, 0, buffer.Length);
        // 輸出結(jié)果
        Console.WriteLine(BitConverter.ToString(buffer)); // 輸出:00-00-00-00-00

        // 定位
        memoryStream.Position = 0;

        // 再次讀取數(shù)據(jù)
        bytesRead = memoryStream.Read(buffer, 0, buffer.Length);
        Console.WriteLine(BitConverter.ToString(buffer)); // 輸出:48 65 6C 6C 6F
        Console.WriteLine(Encoding.UTF8.GetString(buffer)); //輸出:Hello

        // 清空 MemoryStream
        memoryStream.SetLength(0);
        memoryStream.Position = 0;

        // 檢查是否清空
        Console.WriteLine("MemoryStream length after clear: " + memoryStream.Length); // 輸出:MemoryStream length after clear: 0
    }
}

通過這個示例,我們可以看到 MemoryStream 在處理內(nèi)存中的數(shù)據(jù)流時是多么靈活和有用。它不僅可以用于臨時存儲數(shù)據(jù),還可以用于實現(xiàn)復(fù)雜的數(shù)據(jù)處理邏輯。

三、MemoryStream的高級使用

1. 數(shù)據(jù)交互

1)與文本數(shù)據(jù)交互

使用 StreamReader/StreamWriter

public class Program
{
    public static void Main(string[] args)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            // 創(chuàng)建一個StreamWriter,用于向MemoryStream寫入字符串
            using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8, 1024, leaveOpen: true))
            {
                // leaveOpen: true 的作用:
                // 防止 StreamWriter 關(guān)閉時連帶關(guān)閉底層的 MemoryStream,確保后續(xù) StreamReader 可正常操作流
                sw.WriteLine("Hello, World!");
                sw.WriteLine("This is a test.");
            }
            // 將MemoryStream的位置重置到開頭
            ms.Seek(0, SeekOrigin.Begin);

            // 創(chuàng)建一個StreamReader,用于從MemoryStream讀取字符串
            using (StreamReader sr = new StreamReader(ms, Encoding.UTF8))
            {
                string line;
                while ((line = sr.ReadLine()) != null)
                {
                    Console.WriteLine(line);
                }
            }
        }
    }
}

在這個例子中,我們首先創(chuàng)建了一個MemoryStream實例,然后使用StreamWriterMemoryStream寫入了兩行字符串。寫入完成后,我們將MemoryStream的位置重置到開頭,接著使用StreamReaderMemoryStream讀取字符串并打印到控制臺。

2)與二進制數(shù)據(jù)交互

使用 BinaryReader/BinaryWriter

public class Program
{
    public static void Main(string[] args)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            // 創(chuàng)建一個BinaryWriter,用于向MemoryStream寫入二進制數(shù)據(jù)
            using (BinaryWriter writer = new BinaryWriter(ms,Encoding.UTF8,leaveOpen:true))
            {
                // leaveOpen: true 的作用:
                // 防止 StreamWriter 關(guān)閉時連帶關(guān)閉底層的 MemoryStream,確保后續(xù) StreamReader 可正常操作流
                
                writer.Write(32);// 寫入整數(shù)
                writer.Write(12.3f);// 寫入單精度浮點數(shù)
                writer.Write("This is a test.");// 寫入字符串
            }

            // 將MemoryStream的位置重置到開頭
            ms.Seek(0, SeekOrigin.Begin);

            // 創(chuàng)建一個 BinaryReader,用于從MemoryStream讀取二進制數(shù)據(jù)
            using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8))
            {
                Console.WriteLine(reader.ReadInt32());  //輸出:32
                Console.WriteLine(reader.ReadSingle()); //輸出:12.3
                Console.WriteLine(reader.ReadString()); //輸出:This is a test.
            }
        }
    }
}

3)與網(wǎng)絡(luò)流交互

將內(nèi)存流作為網(wǎng)絡(luò)傳輸?shù)木彌_區(qū):

// 服務(wù)端接收數(shù)據(jù)
NetworkStream ns = client.GetStream();
MemoryStream ms = new MemoryStream();
ns.CopyTo(ms); // 將網(wǎng)絡(luò)流復(fù)制到內(nèi)存流
byte[] buffer = ms.ToArray();

2. 高級技巧

1)及時釋放資源

使用using語句:確保流對象及時釋放,避免內(nèi)存泄漏。

using (MemoryStream stream = new MemoryStream()) { /*...*/ }

2)設(shè)置初始容量

預(yù)分配容量:若已知數(shù)據(jù)大小,初始化時指定Capacity減少動態(tài)擴容開銷。

頻繁寫入數(shù)據(jù)時,指定初始容量可避免內(nèi)存頻繁擴容:

// 預(yù)分配 1MB 內(nèi)存
using (MemoryStream ms = new MemoryStream(1024 * 1024))
{
    // 寫入大量數(shù)據(jù)
}

3)重置流以復(fù)用內(nèi)存

通過 SetLength(0)Seek 方法或 設(shè)置Position重置流:

using (MemoryStream ms = new MemoryStream())
{
    ms.Write(data, 0, data.Length);
    
    // 重置流并清空內(nèi)容
    ms.SetLength(0); 
    ms.Position = 0; //或 ms.Seek(0, SeekOrigin.Begin);
    
    // 重新寫入新數(shù)據(jù)
    ms.Write(newData, 0, newData.Length);
}

3. 實際應(yīng)用示例

1)使用MemoryStream序列化和反序列化對象

using System;
using System.Text;
using System.Text.Json;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

class Program
{
    static void Main()
    {
        Person person = new Person { Name = "John Doe", Age = 30 };
        // 序列化對象到MemoryStream
        using (MemoryStream ms = new MemoryStream())
        {
            JsonSerializer.Serialize(ms, person);
            // 將MemoryStream的位置重置到開頭
            ms.Seek(0, SeekOrigin.Begin);
            // 反序列化對象從MemoryStream
            Person deserializedPerson = JsonSerializer.Deserialize<Person>(ms);
            Console.WriteLine("Name: " + deserializedPerson.Name);  // 輸出:Name: John Doe
            Console.WriteLine("Age: " + deserializedPerson.Age);    // 輸出:Age: 30
        }
    }
}

2)使用MemoryStream作為臨時緩沖區(qū)

using System;
using System.IO;
public class MemoryStreamExample
{
    public static void Main()
    {
        // 創(chuàng)建一個MemoryStream作為臨時緩沖區(qū)
        using (MemoryStream ms = new MemoryStream())
        {
            // 寫入一些數(shù)據(jù)到MemoryStream
            byte[] data = new byte[] { 1, 2, 3, 4, 5 };
            ms.Write(data, 0, data.Length);
            // 將MemoryStream作為參數(shù)傳遞給其他方法
            ProcessData(ms);
        }
    }
    public static void ProcessData(MemoryStream ms)
    {
        // 將MemoryStream的位置重置到開頭
        ms.Seek(0, SeekOrigin.Begin);
        // 讀取數(shù)據(jù) from MemoryStream
        byte[] buffer = new byte[ms.Length];
        ms.Read(buffer, 0, buffer.Length);
        Console.WriteLine("Received data:");
        foreach (byte b in buffer)
        {
            Console.Write(b + " ");
        }
    }
}

3)高效處理復(fù)雜場景

案例:日志文件關(guān)鍵字篩選

假設(shè)需要從多個.log文件中提取含特定關(guān)鍵字的行,傳統(tǒng)方法可能導(dǎo)致內(nèi)存暴漲。

優(yōu)化方案

  1. 逐行讀取文件:使用StreamReader避免一次性加載大文件。
  2. 內(nèi)存流緩存匹配行:將匹配的行暫存至MemoryStream,減少磁盤IO次數(shù)。
  3. 批量寫入結(jié)果:最后將內(nèi)存流數(shù)據(jù)一次性寫入目標(biāo)文件。
    List<string> matchedLines = new List<string>();
    foreach (var file in Directory.GetFiles("logs", "*.log")) 
    {
        using (var reader = new StreamReader(file)) 
        {
            while (!reader.EndOfStream) 
            {
                string line = reader.ReadLine();
                if (Regex.IsMatch(line, "keyword")) 
                {
                    matchedLines.Add(line);
                }
            }
        }
    }
    // 使用MemoryStream合并數(shù)據(jù)并寫入文件
    using (MemoryStream ms = new MemoryStream()) 
    {
        byte[] buffer = Encoding.UTF8.GetBytes(string.Join("\n", matchedLines));
        ms.Write(buffer, 0, buffer.Length);
        File.WriteAllBytes("result.txt", ms.ToArray());
    }
    
    實測性能提升顯著

四、常見問題與解決方案

1. 讀取時超出容量

// 錯誤示例:未重置位置導(dǎo)致讀取失敗
using (MemoryStream ms = new MemoryStream())
{
    ms.Write(data, 0, data.Length);
    byte[] buffer = new byte[ms.Length];
    ms.Read(buffer, 0, buffer.Length); // 拋出異常,因為 Position 已在末尾
}

// 正確做法:重置位置
ms.Position = 0;
ms.Read(buffer, 0, buffer.Length);

2. 處理大文件時的內(nèi)存問題

當(dāng)數(shù)據(jù)量超過內(nèi)存限制時,改用 FileStream

// 替代方案:使用文件流
using (FileStream fs = new FileStream("temp.bin", FileMode.Create))
{
    // 寫入數(shù)據(jù)到文件流
}

3. 異步操作

通過 ToArray() 獲取字節(jié)數(shù)組后,可異步處理:

public async Task ProcessAsync()
{
    using (MemoryStream ms = new MemoryStream())
    {
        // 寫入數(shù)據(jù)
        byte[] data = ms.ToArray();
        
        // 異步發(fā)送到網(wǎng)絡(luò)
        await client.SendAsync(data);
    }
}

五、MemoryStream的優(yōu)缺點

優(yōu)點

  • 內(nèi)存中操作速度快:由于數(shù)據(jù)存儲在內(nèi)存中,讀寫速度非常快。
  • 容量靈活MemoryStream的容量可以動態(tài)增長,以適應(yīng)數(shù)據(jù)量的變化。
  • 支持讀寫定位操作:支持CanReadCanWriteCanSeek屬性,便于靈活操作。

缺點

  • 內(nèi)存占用高:處理大量數(shù)據(jù)時,可能會占用大量的內(nèi)存資源。
  • 不適合持久化存儲:數(shù)據(jù)存儲在內(nèi)存中,程序關(guān)閉后數(shù)據(jù)會丟失。

六、最佳實踐總結(jié)

  • 資源管理
    始終使用 using 語句確保流正確釋放:

    using (MemoryStream ms = new MemoryStream()) { ... }
    
  • 位置重置
    寫入后讀取前必須重置 Position0。

  • 性能優(yōu)化

    • 指定初始容量:在創(chuàng)建MemoryStream時,盡量指定初始容量,以減少動態(tài)增長的次數(shù),提高性能。
    • 處理大量數(shù)據(jù)時謹(jǐn)慎使用:處理大量數(shù)據(jù)時,考慮使用文件流或其他適合的流類型,避免內(nèi)存占用過高。(對大文件使用 FileStream 替代。)

參考資料

C# MemoryStream流的詳解與示例
C# Stream 和 byte[] 之間的轉(zhuǎn)換(文件流的應(yīng)用)
C# Stream篇(五) – MemoryStream

到此這篇關(guān)于C# MemoryStream 使用詳解的文章就介紹到這了,更多相關(guān)C# MemoryStream 使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論