[C#]關於Stream的那些事
前言
其實自己對stream的掌握度實在很低,偏偏在很多部份,不管是上傳還是下載等等都跟stream有關係,每次都是有需求來的時候就google一下別人的文章實做,做完了之後就又忘記了,每次要做就要再google一次,不如自己來把學到或實做的記錄下來,方便自己以後查詢。
因為stream相關的,通常都繼承了stream類別,所以大部份的屬性方法都會有,那下面就紀錄一下我從名稱比較猜不出來是要幹嘛的,不過這些都是筆者從網路看來的,理解上如有錯誤再請指正。
Seek :可指定讀取位置,從頭讀取設定為0
Position:取得或設定當前stream的位置,在read之前務必設定為0
Flush:在要關閉stream之前,先調用此方法,把所有stream寫進目標(比如memory)
導覽
- StreadReader(讀取檔案內容)
- StreamWrite(寫入檔案內容)
- StringReader(讀取字串)
- StringWrite(寫入字串)
- FileStream
- DeflateStream(壓縮用,無法用winzip解壓縮)
- GZipStream 類別(壓縮用,可供winzip解壓縮)
- MemoryStream
- 結論
這邊是取了某個隨便記錄的log檔案來做示例,因為我是用linqpad來做的,所以檔案路徑是硬碟的路徑
using (StreamReader sr = new StreamReader(@"C:\Users\anson.shih\Downloads\2017-04-11.txt"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
結果
另一種async的方式
String filename = @"C:\Users\anson.shih\Downloads\2017-04-11.txt";
Char[] buffer;
using (var sr = new StreamReader(filename))
{
buffer = new Char[(int)sr.BaseStream.Length];
await sr.ReadAsync(buffer, 0, (int)sr.BaseStream.Length);
}
Console.WriteLine(new String(buffer));
//取得C槽所有目錄夾的檔案
DirectoryInfo[] cDirs = new DirectoryInfo(@"c:\").GetDirectories();
// 寫進哪個目的地
using (StreamWriter sw = new StreamWriter(@"C:\Users\anson.shih\Downloads\123.txt"))
{
foreach (DirectoryInfo dir in cDirs)
{
sw.WriteLine(dir.Name);
}
}
string line = "";
using (StreamReader sr = new StreamReader(@"C:\Users\anson.shih\Downloads\123.txt"))
{
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
把字串讀出來,如果有斷行的話,也會一行一行的寫出來
string message=@"Characters in 1st line to read
and 2nd line
and the end";
using (StringReader reader = new StringReader(message))
{
string readText = await reader.ReadToEndAsync();
Console.WriteLine(readText);
}
結果
如果要分析字串,並在不同地方加入內容的話,就會用到這個了。
string textReaderText = "資質低落的條件下,只能比別人更努力了";
Console.WriteLine($"原本的字串:{textReaderText}");
StringWriter strWriter = new StringWriter();
StringReader strReader = new StringReader(textReaderText);
string line = string.Empty;
strWriter.Write("kinanson ");
while ((line = strReader.ReadLine()) != null)
{
await strWriter.WriteAsync(line);
}
Console.WriteLine($"修改後的字串:{strWriter}");
結果
下面包含了寫入和讀取字元出來,不過得注意一下,下面的做法是每次都把檔案刪掉再重新新增
string path = @"C:\Users\anson\Downloads\MyTest.txt";
if (File.Exists(path))
{
File.Delete(path);
}
//Create the file.
using (FileStream fs = File.Create(path))
{
await AddText(fs, "This is some text");
await AddText(fs, "This is some more text,");
await AddText(fs, "\r\nand this is on a new line");
await AddText(fs, "\r\n\r\nThe following is a subset of characters:\r\n");
}
async Task AddText(FileStream fs, string value)
{
byte[] info = new UTF8Encoding(true).GetBytes(value);
await fs.WriteAsync(info, 0, info.Length);
}
using (FileStream fs = File.OpenRead(path))
{
byte[] b = new byte[1024]; //這邊代表的是每行讀取的字元
UTF8Encoding temp = new UTF8Encoding(true);
while (fs.Read(b, 0, b.Length) > 0)
{
Console.WriteLine(temp.GetString(b));
}
}
下面則是直接開啟檔案,再寫字進去
UTF8Encoding uniencoding = new UTF8Encoding();
string filename = @"C:\Users\anson\Downloads\MyTest.txt";
byte[] result = uniencoding.GetBytes("what is it");
using (FileStream SourceStream = File.Open(filename, FileMode.OpenOrCreate))
{
SourceStream.Seek(0, SeekOrigin.End);
await SourceStream.WriteAsync(result, 0, result.Length);
}
DeflateStream(壓縮用,無法用winzip解壓縮)
void Main()
{
DirectoryInfo directorySelected = new DirectoryInfo(directoryPath); //取得我指定的資料夾下的所有檔案
Compress(directorySelected);
foreach (FileInfo fileToDecompress in directorySelected.GetFiles("*.cmp")) //取得指定資料夾下的所有cmp檔
{
Decompress(fileToDecompress);
}
}
static string directoryPath = @"C:\Users\anson\Downloads";
public static void Compress(DirectoryInfo directorySelected)
{
foreach (FileInfo fileToCompress in directorySelected.GetFiles("*.txt"))
{
using (FileStream originalFileStream = fileToCompress.OpenRead())
{
using (FileStream compressedFileStream = File.Create(fileToCompress.FullName + ".cmp"))
{
using (DeflateStream compressionStream = new DeflateStream(compressedFileStream,
CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);
}
}
FileInfo info = new FileInfo(directoryPath + "\\" + fileToCompress.Name + ".cmp");
Console.WriteLine("Compressed {0} from {1} to {2} bytes.",
fileToCompress.Name, fileToCompress.Length.ToString(), info.Length.ToString());
}
}
}
public static void Decompress(FileInfo fileToDecompress)
{
using (FileStream originalFileStream = fileToDecompress.OpenRead())
{
string currentFileName = fileToDecompress.FullName;
string newFileName = currentFileName.Remove(currentFileName.Length - fileToDecompress.Extension.Length);
using (FileStream decompressedFileStream = File.Create(newFileName))
{
using (DeflateStream decompressionStream = new DeflateStream (originalFileStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedFileStream);
Console.WriteLine("Decompressed: {0}", fileToDecompress.Name);
}
}
}
}
GZipStream 類別(壓縮用,可供winzip解壓縮)
跟DeflateStream差異不大,換使用的類別,還有壓縮的檔名而已
void Main()
{
DirectoryInfo directorySelected = new DirectoryInfo(directoryPath); //取得我指定的資料夾下的所有檔案
Compress(directorySelected);
foreach (FileInfo fileToDecompress in directorySelected.GetFiles("*.zip")) //取得指定資料夾下的所有zip檔
{
Decompress(fileToDecompress);
}
}
static string directoryPath = @"C:\Users\anson\Downloads";
public static void Compress(DirectoryInfo directorySelected)
{
foreach (FileInfo fileToCompress in directorySelected.GetFiles("*.txt"))
{
using (FileStream originalFileStream = fileToCompress.OpenRead())
{
using (FileStream compressedFileStream = File.Create(fileToCompress.FullName + ".zip"))
{
using (GZipStream compressionStream = new GZipStream (compressedFileStream,
CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);
}
}
FileInfo info = new FileInfo(directoryPath + "\\" + fileToCompress.Name + ".zip");
Console.WriteLine("Compressed {0} from {1} to {2} bytes.",
fileToCompress.Name, fileToCompress.Length.ToString(), info.Length.ToString());
}
}
}
public static void Decompress(FileInfo fileToDecompress)
{
using (FileStream originalFileStream = fileToDecompress.OpenRead())
{
string currentFileName = fileToDecompress.FullName;
string newFileName = currentFileName.Remove(currentFileName.Length - fileToDecompress.Extension.Length);
using (FileStream decompressedFileStream = File.Create(newFileName))
{
using (GZipStream decompressionStream = new GZipStream (originalFileStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedFileStream);
Console.WriteLine("Decompressed: {0}", fileToDecompress.Name);
}
}
}
}
這個算是很常使用到,有時候我們在處理資料的時候,就會產生所謂的Stream,但我們不想保存資料在實體位置的時候,就需要把資料暫存在memory裡面,等用戶端保存後此memory就要清空,相關範例在web api下載檔案的部份使用到很多,可以另外參考筆者一些文章都有用到(https://dotblogs.com.tw/kinanson/2017/06/03/100246)
其實還有很多stream,但有些可能筆者認為不是很常用到,有些用別種方式實現會更好,所以先紀錄一下我覺得比較有機會用到的部份,如果有任何誤論或更好的建議,再請提供給筆者哦。