[C#]關於Stream的那些事

[C#]關於Stream的那些事

前言

其實自己對stream的掌握度實在很低,偏偏在很多部份,不管是上傳還是下載等等都跟stream有關係,每次都是有需求來的時候就google一下別人的文章實做,做完了之後就又忘記了,每次要做就要再google一次,不如自己來把學到或實做的記錄下來,方便自己以後查詢。

因為stream相關的,通常都繼承了stream類別,所以大部份的屬性方法都會有,那下面就紀錄一下我從名稱比較猜不出來是要幹嘛的,不過這些都是筆者從網路看來的,理解上如有錯誤再請指正。

Seek :可指定讀取位置,從頭讀取設定為0

Position:取得或設定當前stream的位置,在read之前務必設定為0

Flush:在要關閉stream之前,先調用此方法,把所有stream寫進目標(比如memory)

導覽

  1. StreadReader(讀取檔案內容)
  2. StreamWrite(寫入檔案內容)
  3. StringReader(讀取字串)
  4. StringWrite(寫入字串)
  5. FileStream
  6. DeflateStream(壓縮用,無法用winzip解壓縮)
  7. GZipStream 類別(壓縮用,可供winzip解壓縮)
  8. MemoryStream
  9. 結論

StreadReader( 讀取檔案內容用的 )

這邊是取了某個隨便記錄的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));

StreamWrite(寫入檔案內容)

//取得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);
	}
}

StringReader(讀取字串)

把字串讀出來,如果有斷行的話,也會一行一行的寫出來

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);
}

結果

StringWrite(寫入字串)

如果要分析字串,並在不同地方加入內容的話,就會用到這個了。

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}");

結果

FIleStream

下面包含了寫入和讀取字元出來,不過得注意一下,下面的做法是每次都把檔案刪掉再重新新增

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);
			}
		}
	}
}

MemoryStream

這個算是很常使用到,有時候我們在處理資料的時候,就會產生所謂的Stream,但我們不想保存資料在實體位置的時候,就需要把資料暫存在memory裡面,等用戶端保存後此memory就要清空,相關範例在web api下載檔案的部份使用到很多,可以另外參考筆者一些文章都有用到(https://dotblogs.com.tw/kinanson/2017/06/03/100246)

結論

其實還有很多stream,但有些可能筆者認為不是很常用到,有些用別種方式實現會更好,所以先紀錄一下我覺得比較有機會用到的部份,如果有任何誤論或更好的建議,再請提供給筆者哦。