1. 程式人生 > >BinaryWriter和BinaryReader和MemoryStream類讀寫記憶體

BinaryWriter和BinaryReader和MemoryStream類讀寫記憶體

C#的FileStream類提供了最原始的位元組級上的檔案讀寫功能,但我們習慣於對字串操作,於是StreamWriter和 StreamReader類增強了FileStream,它讓我們在字串級別上操作檔案,但有的時候我們還是需要在位元組級上操作檔案,卻又不是一個位元組 一個位元組的操作,通常是2個、4個或8個位元組這樣操作,這便有了BinaryWriter和BinaryReader類,它們可以將一個字元或數字按指定 個數位元組寫入,也可以一次讀取指定個數位元組轉為字元或數字。

1.BinaryWriter類

BinaryWriter類以二進位制形式將基元型別寫入流,並支援用特定的編碼寫入字串。

常用的方法:

Close      關閉當前的BinaryWriter和基礎流

Seek       設定當前流中的位置

Write      將值寫入當前流

2.BinartReader類

BinartReader類用特定的編碼將基元資料型別讀作二進位制值。

常用的方法:

Close         關閉當前閱讀器及基礎流

Read          從基礎流中讀取字元,並提升流的當前位置

ReadBytes     從當前流將count個位元組讀入位元組陣列,並使當前位置提升count個位元組

ReadInt32     從當前流中讀取4個位元組有符號整數,並使流的當前位置提升4個位元組

ReadString    從當前流讀取一個字串。字串有長度字首,一次7位地被編碼為整數


下面看一個例項:

  1. using UnityEngine;
  2. using System;
  3. using System.Text;
  4. using System.IO;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. public class FileOperator : MonoBehaviour {
  8. // Use this for initialization
  9. void Start () {
  10. WriteFile ();
  11. ReadFile();
  12. }
  13. void ReadFile() // 讀取檔案
  14. {
  15. FileStream fs = new
    FileStream ("D:\\MemoryStreamTest.txt", FileMode.Open, FileAccess.Read);
  16. BinaryReader r = new BinaryReader (fs);
  17. //以二進位制方式讀取檔案中的內容
  18. int i = r.ReadInt32 ();
  19. float f = r.ReadSingle ();
  20. double d = r.ReadDouble ();
  21. bool b = r.ReadBoolean ();
  22. string s = r.ReadString();
  23. Debug.Log (i);
  24. Debug.Log (f);
  25. Debug.Log (d);
  26. Debug.Log (b);
  27. Debug.Log (s);
  28. r.Close ();
  29. fs.Close ();
  30. }
  31. void WriteFile() // 寫入檔案
  32. {
  33. FileStream fs = new FileStream ("D:\\BinaryStreamTest.txt", FileMode.OpenOrCreate);
  34. BinaryWriter w = new BinaryWriter (fs);
  35. //以二進位制方式向建立的檔案中寫入內容
  36. w.Write (666); // 整型
  37. w.Write (66.6f); // 浮點型
  38. w.Write (6.66); // double型
  39. w.Write(true); // 布林型
  40. w.Write ("六六六"); // 字串型
  41. w.Close ();
  42. fs.Close();
  43. }
  44. }

MemoryStream和BufferedStream都派生自基類Stream,因此它們有很多共同的屬性和方法,但是每一個類都有自己獨特的用法。這兩個類都是實現對記憶體進行資料讀寫的功能,而不是對永續性儲存器進行讀寫。

讀寫記憶體-MemoryStream類

MemoryStream類用於向記憶體而不是磁碟讀寫資料。MemoryStream封裝以無符號位元組陣列形式儲存的資料,該陣列在建立MemoryStream物件時被初始化,或者該陣列可建立為空陣列。可在記憶體中直接訪問這些封裝的資料。記憶體流可降低應用程式中對臨時緩衝區和臨時檔案的需要。

下表列出了MemoryStream類的重要方法:

1、Read():讀取MemoryStream流物件,將值寫入快取區。

2、ReadByte():從MemoryStream流中讀取一個位元組。

3、Write():將值從快取區寫入MemoryStream流物件。

4、WriteByte():從快取區寫入MemoytStream流物件一個位元組。

Read方法使用的語法如下:

mmstream.Read(byte[] buffer,offset,count) 

其中mmstream為MemoryStream類的一個流物件,3個引數中,buffer包含指定的位元組陣列,該陣列中,從offset到(offset +count-1)之間的值由當前流中讀取的字元替換。Offset是指Buffer中的位元組偏移量,從此處開始讀取。Count是指最多讀取的位元組數。Write()方法和Read()方法具有相同的引數型別。

MemoryStream類的使用例項:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO;
  6. namespace ConsoleApplication1
  7. {
  8. class Program
  9. {
  10. static void Main()
  11. {
  12. int count;
  13. byte[] byteArray;
  14. char[] charArray;
  15. UnicodeEncoding uniEncoding = new UnicodeEncoding();
  16. // Create the data to write to the stream.
  17. byte[] firstString = uniEncoding.GetBytes("一二三四五");
  18. byte[] secondString = uniEncoding.GetBytes("上山打老虎");
  19. using (MemoryStream memStream = new MemoryStream(100))
  20. {
  21. // Write the first string to the stream.
  22. memStream.Write(firstString, 0, firstString.Length);
  23. // Write the second string to the stream, byte by byte.
  24. count = 0;
  25. while (count < secondString.Length)
  26. {
  27. memStream.WriteByte(secondString[count++]);
  28. }
  29. // Write the stream properties to the console.
  30. Console.WriteLine("Capacity={0},Length={1},Position={2}\n", memStream.Capacity.ToString(), memStream.Length.ToString(), memStream.Position.ToString());
  31. // Set the position to the beginning of the stream.
  32. memStream.Seek(0, SeekOrigin.Begin);
  33. // Read the first 20 bytes from the stream.
  34. byteArray = new byte[memStream.Length];
  35. count = memStream.Read(byteArray, 0, 20);
  36. // Read the remaining bytes, byte by byte.
  37. while (count < memStream.Length)
  38. {
  39. byteArray[count++] = Convert.ToByte(memStream.ReadByte());
  40. }
  41. // Decode the byte array into a char array
  42. // and write it to the console.
  43. charArray = new char[uniEncoding.GetCharCount(byteArray, 0, count)];
  44. uniEncoding.GetDecoder().GetChars(byteArray, 0, count, charArray, 0);
  45. Console.WriteLine(charArray); Console.ReadKey();
  46. }
  47. }
  48. }
  49. }

在這個例項程式碼中使用了using關鍵字。注意:

using 關鍵字有兩個主要用途:

1、作為指令,用於為名稱空間建立別名或匯入其他名稱空間中定義的型別。

例如:

  1. using System; 

2、作為語句,用於定義一個範圍,在此範圍的末尾將釋放物件。

  1. using(Connection conn=new Connection(connStr))  
  2. {  
  3. }  
  4. //使用using關鍵字可及時銷燬物件 

MemoryStream.Capacity 屬性 取得或設定配置給這個資料流的位元組數目。

MemoryStream.Position 屬性 指定當前流的位置。

MemoryStream.Length 屬性獲取用位元組表示的流長度。

SeekOrigin()是一個列舉類,作用設定流的一個引數。

SeekOrigin.Begin我得理解就是檔案的最開始,“0”是偏移,表示跳過0個位元組。寫2就是跳過2個位元組。

MemoryStream類通過位元組讀寫資料。本例中定義了寫入的位元組陣列,為了更好的說明Write和WriteByte的異同,在程式碼中聲明瞭兩個byte陣列,其中一個數組寫入時呼叫Write方法,通過指定該方法的三個引數實現如何寫入。

另一個數組呼叫了WriteByte方法,每次寫入一個位元組,所以採用while迴圈來完成全部位元組的寫入。寫入MemoryStream後,可以檢索該流的容量,實際長度,當前流的位置,將這些值輸出到控制檯。通過觀察結果,可以確定寫入MemoryStream流是否成功。

呼叫Read和ReadByte兩種方法讀取MemoryStream流中的資料,並將其進行Unicode編碼後輸出到控制檯。






讀取記憶體流中的資料


在.NET中,使用抽象基類System.IO.Stream代表流,它提供Read和Write兩個方法。由於資料流的有序性,因此流物件還有一個讀寫指標,為此,Stream類還有一個Seek方法用於移動讀寫指標。  

字串與位元組陣列間的互相轉化:
  1. string str = "記憶體大小";
  2. byte[] temp = Encoding.UTF8.GetBytes (str); // 字串轉化為位元組陣列
  3. string s = Encoding.UTF8.GetString (temp); // 位元組陣列轉化為字串
  4. Debug.Log (s);

Encoding用法比較簡單,如果只是位元組和字元的互相轉換,GetBytes()和GetChars()這兩個方法及它們的過載基本上會滿足你所有要求。

GetByteCount()及其過載是得到一個字串轉換成位元組時實際的位元組個數。

GetCharCount()及其過載是得到一個位元組陣列轉換成字串的大小。

Decoder.GetChars 方法

在派生類中重寫時,將指定位元組陣列的位元組序列和內部緩衝區中的任何位元組解碼到指定的字元陣列。
在派生類中重寫時,將一個位元組序列解碼為一組字元。

Java裡一個byte取值範圍是-128~127, 而C#裡一個byte是0~255.

首位不同. 但是底層I/O儲存的資料是一樣的

FileStream物件的資料來自檔案,而MemoryStream物件的資料來自記憶體緩衝區。這兩個類都繼承自Stream類。 

MemoryStream的資料來自記憶體中的一塊連續區域,這塊區域稱為“緩衝區(Buffer)”。可以把緩衝區看成一個數組,每個陣列元素可以存放一個位元組的資料。

  1. 在建立MemoryStream物件時,可以指定緩衝區的大小,並且可以在需要的時候更改。
  2. //位元組陣列
  3. byte[] buffer = new byte[600];
  4. //填充位元組陣列
  5. private void CreateExampleData()
  6. {
  7. for(int i=0; i<600; i++)
  8. {
  9. //byte型別的數最大不能超過255,用256取模實現
  10. buffer[i] = (byte)(i%256);
  11. }
  12. }
  13. 記憶體流的基本使用方法:
  14. private void OnTestMemory()
  15. {
  16. //建立測試資料
  17. CreateExampleData();
  18. //建立記憶體流物件,初始分配50位元組的緩衝區
  19. MemoryStream mem = new MemoryStream(50);
  20. //向記憶體流中寫入位元組陣列的所有資料
  21. mem.Write(buffer,0,buffer.GetLength(0));
  22. MessageBox.Show("寫入資料後的記憶體流長度:" + mem.Length.ToString());
  23. MessageBox.Show("分配給記憶體流的緩衝區大小:" + mem.Capacity.ToString());
  24. mem.SetLength(550);
  25. MessageBox.Show("呼叫SetLength方法後的記憶體流長度:" + mem.Length.ToString());
  26. mem.Capacity = 620;//此值不能小於Length屬性
  27. MessageBox.Show("呼叫Capacity方法後緩衝區大小:" + mem.Capacity.ToString());
  28. //將讀寫指標移到距流開頭10個位元組的位置
  29. mem.Seek(10,SeekOrigin.Begin);
  30. MessageBox.Show(mem.ReadByte().ToString());
  31. mem.Close();
  32. }
  33. 記憶體流的Length屬性代表了其中存放的資料的真實長度,而Capacity屬性則代表了分配給記憶體流的記憶體空間大小。
  34. 可以使用位元組陣列建立一個固定大小的MemoryStream,
  35. MemoryStream mem = new MemoryStream(buffer);
  36. 這時,無法再設定Capacity屬性的大小。
  37. 還可以建立只讀的記憶體流物件。
  38. MemoryStream mem = new MemoryStream(buffer,false);
  39. FlieStream用於存取檔案。
  40. 建立檔案並寫入內容:
  41. //建立一個新檔案
  42. FileStream fsForWrite = new FileStream("test.data",FileMode.Create);
  43. try
  44. {
  45. //寫入一個位元組
  46. fsForWrite.WriteByte(100);
  47. CreateExampleData();
  48. //將位元組陣列寫入檔案
  49. fsForWrite.Write(buffer,0,buffer.GetLength(0));
  50. }
  51. catch(Exception ex)
  52. {
  53. MessageBox.Show(ex.Message);
  54. }
  55. finally
  56. {
  57. //關閉檔案
  58. fsForWrite.Close();
  59. }
  60. 開啟檔案並讀取內容:
  61. private void ReadFromFile()
  62. {
  63. FileStream fsForRead = new FileStream("test.data",FileMode.Open);
  64. try
  65. {
  66. //讀入一個位元組
  67. MessageBox.Show("檔案的第一個位元組為:"+fsForRead.ReadByte().ToString());
  68. //讀寫指標移到距開頭10個位元組處
  69. fsForRead.Seek(10,SeekOrigin.Begin);
  70. byte[] bs = new byte[10];
  71. //從檔案中讀取10個位元組放到陣列bs中
  72. fsForRead.Read(bs,0,10);
  73. }
  74. catch(Exception ex)
  75. {
  76. MessageBox.Show(ex.Message);
  77. }
  78. finally
  79. {
  80. fsForRead.Close(); }
  81. }
  82. 如果一個程式退出了,但它開啟的檔案沒有被關閉,將導致其他程式無法修改或刪除此檔案。
FileStream與MemoryStream間的相互作用:
-----解決方案--------------------
  FileStream fs = new FileStream(path, FileMode.Open); 
  byte[] data = new byte[fs.Length]; 
  fs.Read(data, 0, data.Length); 
  fs.Close();
  MemoryStream ms = new MemoryStream(data);

------解決方案--------------------

 ///定義並例項化一個記憶體流,以存放圖片的位元組陣列。
MemoryStream m = new MemoryStream();
///獲得當前路徑
string strAppPath = AppDomain.CurrentDomain.BaseDirectory; //獲得可執行檔案的路徑。
///獲得圖片路徑 
string strPath = strAppPath + "img\\default.jpg"; 
///圖片讀入FileStream 
FileStream f = new FileStream(strPath, FileMode.open); 
///把FileStream寫入MemoryStream 
m.SetLength(f.Length); 
f.Read(m.GetBuffer(), 0, (int)f.Length); 
m.Flush(); 
f.Close();

------解決方案--------------------
            FileStream fs = new FileStream(fileName, FileMode.Open);
            byte[] MyData = new byte[fs.Length];
            fs.Read(MyData, 0, (int)fs.Length);
            fs.Close();
            MemoryStream ms = new MemoryStream(MyData);

------解決方案--------------------
MemoryStream ms = new MemoryStream(File.ReadAllBytes("c:\\1.jpg"));