數據流與數據的加密和解密
數據編碼和解碼
常見的字符集編碼方式
ASCII
ASCII字符集由128個字符組成,包括大小寫字母、數字0~9、標點符號、非
打印字符(換行符、製表符等4個)以及控制字符(退格、響鈴等)。
Unicode
Unicode是國際通用的編碼方式,可以表示地球上絕大部分地區的文字。這種編
碼每個字符都佔2個字節,例如一個英文字符佔2個字節,一個漢字也是2個字節。
C#中的字符和字符串默認採用的都是Unicode編碼。
UTF-8
UTF-8是在因特網上使用最廣泛的一種編碼格式。它是Unicode的一種變長字符
編碼,用1~4個字節表示一個Unicode字符。例如,每個英文字母都佔1個字節,
每個漢字都佔4個字節。
GB2312和GB18030
對於簡體中文來說,國家規定的編碼標準(國標)有兩種,一種是GB2312
(1980年公佈),另一種是GB18030(2000年公佈)
GB2312每個漢字的編碼長度都佔2個字節,這種編碼方式最多支持6千多個漢
字的編碼;
GB18030編碼長度爲1~4個字節,可支持兩萬多個漢字的編碼。
利用Encoding類實現編碼和解碼
1.Encoding類位於System.Text命名空間下
2.該類主要用於對字符集進行編碼和解碼以及將一種編碼格式轉換爲另一種編碼格式。
3.Encoding類提供的常用屬性和方法:
4.Encoding類的基本用法:
1.獲取所有編碼名稱及其描述信息
2.獲取指定編碼名稱及其描述信息
3.不同編碼之間的轉換
4.利用Encoding類實現字符串的編碼和解碼
1.獲取所有編碼名稱及其描述信息
使用Encoding類靜態的GetEncodings方法可得到一個包含所有編碼的
EncodingInfo類型的數組。
EncodingInfo類同位於System.Text命名空間下,提供有關編碼的基本信息。
foreach ( EncodingInfo ei in Encoding.GetEncodings( ))
{
Encoding en = ei.GetEncoding( );
Console.WriteLine("編碼名稱:{0,-18},編碼描述:{1}", ei.Name,
en.EncodingName);
}
2.獲取指定編碼名稱及其描述信息
Encoding類提供了UTF8、ASCII、Unicode等屬性,通過這些屬性可以獲取某個
字符集編碼。
也可以利用Encoding類靜態的GetEndcoing方法來獲取。
Encoding ascii = Encoding.ASCII;
Encoding gb2312 = Encoding.GetEncoding("GB2312");
Encoding gb18030 = Encoding.GetEncoding("GB18030");
得到Encoding對象後,即可利用HeaderName屬性獲取編碼名稱,利用EncodingName
屬性獲取編碼描述。
string s1 = "GB2312的編碼名稱爲:" + gb2312.HeaderName;
string s2 = "GB2312的編碼描述爲:" + gb2312.EncodingName;
3.不同編碼之間的轉換
利用Encoding類的Convert方法可將字節數組從一種編碼轉換爲另一種編碼,轉換
結果爲一個byte類型的數組。
語法爲:
public static byte[] Convert(
Encoding srcEncoding, //源編碼
Encoding dstEncoding, //目標編碼
byte[] bytes //待轉換的字節數組
)
如將Unicode字符串轉換爲UTF8字符串
string s = "abcd";
Encoding unicode = Encoding.Unicode;
Encoding utf8 = Encoding.UTF8;
byte[] b = Encoding.Convert(unicode, utf8, unicode.GetBytes(s));
string s1 = utf8.GetString(b);
4.利用Encoding類實現字符串的編碼和解碼
可以直接用Encoding類實現字符串的編碼和解碼
例如:
Encoding en = Encoding.GetEncoding("GB2312");
//編碼
byte[] bytes = en.GetBytes("abcd123");
//按字節顯示編碼後的數據
textBlock1.Text = BitConverter.ToString(bytes);
//解碼
textBlock2.Text = en.GetString(bytes);
數據流
數據流(Stream)是對串行傳輸數據的一種抽象表示。
當希望通過網絡逐字節串行傳輸數據,或者對文件逐字節進行操作時,首先需要將數據轉化爲數據流。
System.IO命名空間下的Stream類是所有數據流的基類。
數據流一般和某個外部數據源相關
數據源可以是硬盤上的文件、外部設備(如I/O卡的端口)、內存、網絡套接字等。
根據不同的數據源,可分別使用從Stream類派生的類對數據流進行操作。
FileStream類、MemoryStream類、NetworkStream類、CryptoStream類
用於文本讀寫的StreamReader和StreamWriter類
用於二進制讀寫的BinaryReader和BinaryWriter類等。
對數據流的操作有3種:
逐字節順序寫入(將數據從內存緩衝區傳輸到外部源)
逐字節順序讀取(將數據從外部源傳輸到內存緩衝區)
隨機讀寫(從某個位置開始逐字節順序讀或寫)。
文件流
System.IO命名空間下的FileStream類繼承於Stream類
利用FileStream類可以對各種類型的文件進行讀寫
例如:文本文件、可執行文件、圖像文件、視頻文件等
創建FileStream對象
常用有兩種創建FileStream對象的辦法。
(1)利用構造函數創建FileStream對象
利用FileStream類的構造函數創建FileStream對象
語法爲
FileStream (string path, FileMode mode, FileAccess access)
參數中的path指定文件路徑;
mode指定文件操作方式;
access控制文件訪問權限。
FileMode枚舉的可選值:CreateNew、Create、Open、OpenOrCreate、Truncate、
Append
FileAccess枚舉的可選值有:Read、Write、ReadWrite
(2)利用File類創建FileStream對象
FileMode
FileAccess
(2)利用File類創建FileStream對象
利用System.IO命名空間下的File類創建FileStream對象。
利用OpenRead方法創建僅讀取的文件流;
利用OpenWrite方法創建僅寫入的文件流。
如,以僅讀取的方式打開File1.txt文件。
FileStream fs= File.OpenRead(@"D:\ls\File1.txt");
讀寫文件:
得到FileStream對象後:
可以利用該對象的Read方法讀取文件數據到字節數組中
利用Write方法將字節數組中的數據寫入文件
(1)Read方法
FileStream對象的Read方法用於將文件中的數據讀到字節數組中
語法如下,該方法返回從FileStream中實際讀取的字節數。
public override int Read(
byte[] array, //保存從文件流中實際讀取的數據
int offset, // 向array數組中寫入數據的起始位置,一般爲0
int count //希望從文件流中讀取的字節數
)
(2)Write方法
FileStream對象的Write方法用於將字節數組寫入到文件中
語法如下:
public override void Write(
byte[] buffer, //要寫入到文件流中的數據
int offset, //從buffer中讀取的起始位置
int size //寫入到流中的字節數
內存流
利用System.IO命名空間下的MemoryStream類,可以按內存流的方式對保存在內存中
的字節數組進行操作:
利用Write方法將字節數組寫入到內存流中
利用Read方法將內存流中的數據讀取到字節數組中
MemoryStream的用法與文件流的用法相似,支持對數據流的查找和隨機訪問。
該對象的CanSeek屬性值默認爲true
程序中可通過Position屬性獲取內存流的當前位置。
由於內存流的容量可自動增長,因此在數據加密以及對長度不定的數據進行緩存
等場合,使用內存流比較方便。
網絡流
寫入操作是指從來源端內存緩衝區到網絡上的數據傳輸;
讀取操作是從網絡上到接收端內存緩衝區(如字節數組)的數據傳輸。
使用NetworkStream對象時,需要注意以下幾點:
通過DataAvailable屬性,可查看緩衝區中是否有數據等待讀出。
網絡流沒有當前位置的概念,不支持對數據流的查找和隨機訪問,NetworkStream
對象的CanSeek屬性始終返回false
讀取Position屬性和調用Seek方法時,都會引發NotSupportedException異常。
1.獲取NetworkStream對象
有兩種獲取NetworkStream對象的辦法。
(1)利用TcpClient對象的GetStream方法得到網絡流對象。
TcpClient tcpClient=new TcpClient( );
tcpClient.Connect("www.abcd.com", 51888);
NetworkStream networkStream = tcpClient.GetStream( );
(2)利用Socket得到網絡流對象。
NetworkStream myNetworkStream = new NetworkStream(mySocket);
2.發送數據
NetworkStream類的Write/Read方法的語法格式和文件流相同
Write方法爲同步方法,在將數據寫入到網絡流之前,Write方法將一直處於阻塞
狀態,直到發送成功或者返回異常爲止
如:檢查NetworkStream是否可寫,如果可寫,則使用Write寫入一條消息。
if (myNetworkStream.CanWrite)
{
byte[] writeBuffer = Encoding.UTF8.GetBytes("Hello");
myNetworkStream.Write(writeBuffer, 0, writeBuffer.Length);
}
else
{ ...... }
3.接收數據
接收方通過調用Read方法將數據從接收緩衝區讀取到進程緩衝區,完成讀取操作。
如:使用DataAvailable來確定是否有數據可供讀取,當有可用數據時,將從
NetworkStream讀取數據。
if(myNetworkStream.CanRead)
{
byte[] readBuffer = new byte[1024]; //設置緩衝區大小
int numberOfBytesRead = 0;
// 準備接收的信息有可能會大於1024,所以要用循環
do{
numberOfBytesRead = myNetworkStream.Read(readBuffer, 0,
readBuffer.Length);
...... //處理接收到數據
}while(myNetworkStream.DataAvailable);
}
else
{ ...... }
加密流
CryptoStream類位於System.Security.Cryptography命名空間下
該類可按加密流的方式加密或者解密數據,而且只能用於對稱加密。
實現CryptoStream的任何被加密的對象都可以和實現Stream的任何對象鏈接起來,
因此一個對象的流式處理輸出可以饋送到另一個對象的輸入,而不需要分別存儲
中間結果。
調用構造函數創建CryptoStream對象時,需用目標數據流、要使用的轉換和流的
模式初始化CryptoStream類的新實例。加密時爲寫訪問模式,解密時爲讀訪問模式。
CryptoStream類的構造函數語法如下:
public CryptoStream(
Stream stream, //對其執行加密轉換的流
ICryptoTransform transform, //要對流執行的加密轉換
CryptoStreamMode mode
//CryptoStreamMode枚舉,有Read和Write兩種
)
使用CryptoStream對象時,一般還要藉助其他流進行處理。
比如使用FileStream作爲目標數據流,
再根據創建的CryptoStream對象生成StreamWriter對象,
然後調用WriteLine方法,通過CryptoStream將加密後的數據寫入
FileStream,
寫入完成後,關閉創建的對象。
此時在文件中保存的就是加密後的數據。
解密時,使用和加密時相同的密鑰創建CryptoStream實例,並在創建該實例時將構造
函數的mode參數改爲讀模式,再將StreamWriter替換成StreamReader,即可將解密
後的數據讀取出來。
StreamReader和StreamWriter類
1.創建StreamReader和StreamWriter的實例
如果數據來源是文件流、內存流或者網絡流,可以利用StreamReader和
StreamWriter對象的構造函數得到讀寫流。
NetworkStream networkStream = client.GetStream( ); StremReader sr =
new StremReader (networkStream);
......
StreamWriter sw = new StreamWriter (networkStream);
......
如果需要處理的是文件流,還可以直接利用文件路徑創建StreamWriter對象。
StreamWriter sw= new StreamWriter ("C:\\file1.txt");
與該方法等價的有File及FileInfo類提供的CreateText方法。
StreamWriter sw = File.CreateText ("C:\\file1.txt");
2.讀寫文本數據
利用StreamWriter類,可以用類似Console.Write和Console.WriteLine的辦法
寫入文本數據.
利用StreamReader類,用類似Console.Read和Console.ReadLine的辦法讀取文本
數據。
讀寫完成後,不要忘記用Close方法關閉流,或者用using語句讓系統自動關閉它。
2.讀寫文本數據
利用StreamWriter類,可以用類似Console.Write和Console.WriteLine的辦法
寫入文本數據.
利用StreamReader類,用類似Console.Read和Console.ReadLine的辦法讀取文本
數據。
讀寫完成後,不要忘記用Close方法關閉流,或者用using語句讓系統自動關閉它。
BinaryReader和BinaryWriter類
System.IO命名空間還提供了BinaryReader和BinaryWriter類以二進制模式讀寫流,
更方便於對圖像文件、壓縮文件等二進制數據進行操作。
對於BinaryReader中的每個讀方法,在BinaryWriter中都有一個與之對應的寫
方法。
比如BinaryReader提供了ReadByte、ReadBoolean、ReadInt、ReadInt16、
ReadDouble、ReadString等方法
BinaryWriter則提供了多個重載的Write方法分別與之對應。
例如,當Write方法傳遞的參數爲Int32類型時,利用BinaryWriter類的Write方
法可以將Int32類型數據轉化爲長度爲4的字節數組,並將字節流傳遞給一個Stream對
象。
與之對應BinaryWriter則提供了多個重載的Write方法分別與之對應。