玩轉你的記憶體 -- 使用TMemoryStream |
|
GrandRURU
站務副站長 發表:240 回覆:1680 積分:1874 註冊:2005-06-21 發送簡訊給我 |
雖然是1998年的文章,但2010年看到這篇文章的我,還是覺得有很大的收獲,在這邊分享出來,翻譯真是門很辛苦的工作呀!
原文:Manipulating memory with TMemoryStream 譯文:愛抱怨本鋪. eden的雜念溜 Manipulating memory with TMemoryStreamC Builder Developer's Journal 雜誌3月號裡有篇"File I/O",講述TFileStream類別和如何使用該類別讀寫在磁碟中的檔案。 但有些時候,與其使用磁碟,你會比較想在記憶體中處理較大的資料。好消息是TFileStream有個親戚叫TMemoryStream,正好是為此目的而設計。 在本篇文章中,我們將展示如何使用TMemoryStream去處理記憶體,就和我們3月刊的檔案處理方法一樣。 我們將證明如何使用與檔案I/O相同的方法去讀寫記憶體。 聽起來不錯!但為什麼我要這樣做啊?你可能會想將檔案分配到記憶體內而不會立即顯示出來。 有很多種很棒的理由讓你選擇它,但最令人在意的還是它的執行速度。 不論何時操作資料,關心的還是它的執行速度。 比方說…舉個例子,你想在程式中的特定時間點進行資料儲存。 但磁碟無法在寫入檔案的同時去處理不必要的工作。 相反的,你可以寫入資料到記憶體內,之後再從該記憶體複製資料到磁碟內,完成即時處理的需求。 假設:有時你會想從磁碟中讀取檔案,做一些處理(比方說像是加密/解密處理),然後再寫回檔案中。 每次寫入檔案時都會很快速的讀取檔案到記憶體中,並且修改及組合資料。 最後,假如你一直是以TMemo來操作字串資料和複製資料,你會知道這程序是如此之慢。 同樣的選擇,你可以讀取字串到記憶體內,並且在記憶體內進行工作,只把最終的結果複製到TMemo。 我們將展示這個小範例…嗯,我們還是先來看TMemoryStream所能提供的東西吧。TMemoryStream basicsTMemoryStream提供可以在記憶體中工作的成員和方法。表A裡是它主要的功能。Table A: Important TMemoryStream properties and methods
TMemoryStream的概念大致上類似TFileStream(詳細的資料可以參考3月刊的"File I/O")。 Loadxxx和Savexxx方法可以輕易地存取檔案和資料流到記憶體中。下面的範例是從磁碟讀出資料,並操作記憶體資料寫回到磁碟中: [code cpp] TMemoryStream* stream = new TMemoryStream; stream->LoadFromFile(OpenDialog1->FileName); stream->Position = 0; for (unsigned int i=0;i char c; stream->Read(&c, 1); c = 32; stream->Position--; stream->Write(&c, 1); } // file is scrambled in memory // later... descramble and display stream->Position = 0; for (unsigned int i=0;i char c; stream->Read(&c, 1); c -= 32; stream->Position--; stream->Write(&c, 1); } stream->Position = 0; Memo1->Lines->LoadFromStream(stream); [/code] SetSize()方法雖然可以配置給資料流固定的記憶體總量。但不一定需要這樣做,因為TMemoryStream會自動分配和釋放所需的記憶體。但如果你需要精確掌握記憶體空間時,仍然可以去設定它,這樣可以在關鍵時刻節省很多Clock週期。 假設,我們試著在某個時間點寫入324kb大小的記憶體資料流。記憶體資料流以每8kb區塊作為一個單位。因此,寫入324kb資料給資料流時,會產生40個記憶體區塊。當我們對所分配好的324kb記憶體資料流空間寫入資料時,不以VCL自動處理記憶體操作下,平均時間會從950ms縮短為570ms,節省的時間可以說是相當地多。 那來個範例吧!最好的學習方式還是來個範例。比方說你想加密一個文字檔。你可以透過改變每個字元的ACSII值去完成這項工作,讓這個文件無法被正常顯示。隨後你可以解譯它後再看看是否真的有還原。或許你也可以將加密/還原前後的結果個別存放到一個Memo元件。整個操作流程大致上如下:[code cpp] TMemoryStream* stream = new TMemoryStream; stream->LoadFromFile(OpenDialog1->FileName); stream->Position = 0; for (unsigned int i=0;i char c; stream->Read(&c, 1); c = 32; stream->Position--; stream->Write(&c, 1); } // file is scrambled in memory // later... descramble and display stream->Position = 0; for (unsigned int i=0;i char c; stream->Read(&c, 1); c -= 32; stream->Position--; stream->Write(&c, 1); } stream->Position = 0; Memo1->Lines->LoadFromStream(stream); [/code] 這個範例程式大致上還蠻簡單明遼,但請注意這行: stream->Position--; 在你從資料流當前的指標去讀取該字元之後,該指標會自動移動到下一個字元。你需要往回移動一個字元以利你將加密後的字元寫入。此外,請注意解密後的資料流可以輕易地透過Memo元件的LoadFromStream函式來讀取它。這個高效能的函式實際上是屬於TStringList的成員。你可以任意地使用字串列舉出的記憶體資料流結合使用它(而且很多的VCL元件也是使用TStringList儲存它們的資料) 列表A和B的程式段包含了加密和解密文字檔,並且將結果顯示在一個Memo元件裡。運作時間會顯示在Label元件中。這程式你可以看出使用Memo元件和使用TMemoryStream類別的結果比較。 [code cpp] //Listing A: STREAMU.H #ifndef StreamUH #define StreamUH //--------------------------------------------- #include #include #include #include #include //--------------------------------------------- class TForm1 : public TForm { __published: // IDE-managed Components TMemo *Memo1; TLabel *Label1; TButton *Button3; TLabel *Label2; TOpenDialog *OpenDialog1; TGroupBox *GroupBox1; TButton *Button1; TButton *Button2; void __fastcall Button1Click(TObject *Sender); void __fastcall Button2Click(TObject *Sender); void __fastcall Button3Click(TObject *Sender); void __fastcall FormCreate(TObject *Sender); private: // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------- extern TForm1 *Form1; //--------------------------------------------- #endif //Listing B: STREAMU.CPP #include #pragma hdrstop #include "StreamU.h" //--------------------------------------------- #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { int start = GetTickCount(); Memo1->Lines-> LoadFromFile(OpenDialog1->FileName); char* buff = Memo1->Lines->GetText(); Screen->Cursor = crHourGlass; for (unsigned int i=0;i c = 32; buff[i] = c; } for (unsigned int i=0;i c -= 32; buff[i] = c; } Memo1->Lines->Text = buff; Screen->Cursor = crDefault; char buff2[20]; sprintf(buff2, "%.02f seconds", (GetTickCount() - start)/1000.0); Label2->Caption = buff2; } //--------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { TMemoryStream* stream = new TMemoryStream; stream->LoadFromFile(OpenDialog1->FileName); Screen->Cursor = crHourGlass; stream->Position = 0; for (unsigned int i=0;i char c; stream->Read(&c, 1); c = 32; stream->Position--; stream->Write(&c, 1); } stream->Position = 0; for (unsigned int i=0;i char c; stream->Read(&c, 1); c -= 32; stream->Position--; stream->Write(&c, 1); } stream->Position = 0; Memo1->Lines->LoadFromStream(stream); Screen->Cursor = crDefault; delete stream; char buff[20]; sprintf(buff, "%.02f seconds", (GetTickCount() - start)/1000.0); Label2->Caption = buff; } //--------------------------------------------- void __fastcall TForm1::Button3Click(TObject *Sender) { if (OpenDialog1->Execute()) { Button1->Enabled = true; Button2->Enabled = true; Memo1->Text = "File Selected: " OpenDialog1->FileName; } } //--------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { Button1->Enabled = false; Button2->Enabled = false; } 使用這個程式時,一定要使用至少大於50kb的文字檔,這樣的結果會比較明顯。你會發現TMemoryStream的高效能,它比Memo元件處理快了將近100倍之多。 結論TMemoryStream是很好很強大的。當你了解如何使用TMemoryStream時,特別是與TFileStream和TStringList結合,新的程式設計前景將會呈現在你的眼前。 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |