CSharp 基礎知識系列-IO篇 流的操作

2020-12-10 程式設計師小高學習筆記

0. 前言

繼續之前的C# IO流,在前幾篇小短片中我們大概看了下C# 的基礎IO也對文件、目錄和路徑的操作有了一定的了解。這一篇開始,給大家演示一下流的各種操作。以文件流為例,一起來看看如何操作吧。

註:之前更新了一篇《Spring Cloud 實戰日記》,這是一個新的系列,有興趣的小夥伴可以從我的帳號首頁進去看看。

1. 簡單的IO流讀寫文件

先來看一部分代碼:

class Program

{

static void Main(string[] args)

{

var directory = Directory.GetCurrentDirectory();

var program = File.Open("../../../Program.cs", FileMode.OpenOrCreate);

// program = File.Open("Program.cs", FileMode.OpenOrCreate);

var buffers = new byte[1024];// 創建一個8k的緩存區

var list = new List<byte>();

while(true)

{

int length = program.Read(buffers, 0, buffers.Length);

if(length <=0)

{

break;

}

list.AddRange(buffers.Take(length));

}

program.Close();

Console.WriteLine(list.Count);

}

}

到目前為止,打開了一個流讀取當前程序源文件,每次讀取到一個字節數組裡,然後將數據放到list集合裡,在讀取完成後關閉這個流。雖然以上流並沒有太多意義,但是基本演示了一下流的讀取操作。

注意到注釋的那行代碼和上一行代碼的區別嗎?在編譯階段,Directory.GetCurrentDirectory()表示源文件所在目錄;在運行階段,表示程序編譯完成的DLL所在目錄。

輸出結果:

以上通過文件流演示了如何讀取一個文件,那麼我們來簡單看看如何通過流寫文件:

class Program

{

static void Main(string[] args)

{

var directory = Directory.GetCurrentDirectory();

var program = File.Open("Program.cs", FileMode.OpenOrCreate);

var buffers = new byte[1024];// 創建一個8k的緩存區

var list = new List<byte>();

while(true)

{

int length = program.Read(buffers, 0, buffers.Length);

if(length <=0)

{

break;

}

list.AddRange(buffers.Take(length));

}

program.Close();

Console.WriteLine($"已讀取:{list.Count}");

var tempr = File.Open("Program_01.cs", FileMode.OpenOrCreate);

tempr.Write(list.ToArray(), 0, list.Count);

tempr.Close();

}

}

以上方法通過讀取當前源碼文件,然後將數據寫入到另一個文件中:」Program_01.cs「。如果運行無誤的話,將會得到一個」Program_01.cs「文件。

2. 使用流適配器

普通的流讀取和寫入都是使用字節數組,這在實際開發中非常不方便,所以C#又在流的基礎上開發了流適配器。C#中流適配器是指XXXReader或者XXXWriter,這種類在初始化的時候傳入一個流作為操作對象,然後對這個流進行一定的封裝,簡化了其操作方法。

現在以StreamReader為例,來看看具體如何使用:

public StreamReader (System.IO.Stream stream);

public StreamReader (System.IO.Stream stream, System.Text.Encoding encoding);

這裡是兩個以流為主要參數的構造方法,不同的是一個指定了文本編碼 encoding,另一個默認使用系統的文本編碼。

public StreamReader (string path);

public StreamReader (string path, System.Text.Encoding encoding);

這兩個是通過指定文件的路徑,然後打開一個StreamReader對象。

現在我們來看下這個Reader對象有哪些方法或者說我們常用的方法有哪些吧:

public override int Read ();

public override int Read (char[] buffer, int index, int count);

讀取字符,與普通的流不同的是,StreamReader的讀取是以字符為單位的讀取,而char類型與int之間存在一定的轉換關係,所以方法Read()的返回值是int。

public override string ReadLine ();

這個方法的意思是一次讀一行,如果讀到末尾則返回null。

public override string ReadToEnd ();

這個方法的意思是一次性讀完剩餘的數據然後返回一個字符串。

照例,Reader提供了流的關閉和銷毀方法:

public override void Close ();

現在讓我們來改造一下第一節的示例程序:

class Program

{

static void Main(string[] args)

{

var reader = new StreamReader("Program.cs");

while(true)

{

var str = reader.ReadLine();

if(str == null)

{

break;

}

Console.WriteLine(str);

}

reader.Close();

}

}

這段代碼的意思是讀取當前主程序的文件,然後按行列印。列印結果應該類似於:

這是我本地的代碼文件。

簡單的介紹了一下StreamReader,然後我們來看一下StreamWriter如何使用。按照我的慣例,先從構造函數來:

public StreamWriter (System.IO.Stream stream);

public StreamWriter (System.IO.Stream stream, System.Text.Encoding encoding);

與StreamReader類似,打開一個允許寫的流。

public StreamWriter (string path);

public StreamWriter (string path, bool append);

public StreamWriter (string path, bool append, System.Text.Encoding encoding);

打開path對應的文件,然後將數據寫入到文件中。append表示當文件存在時,數據是追加到文件末尾還是覆蓋文件。

然後看一下它的方法:

public override void Write (string value);

public override void Write (string format, object arg0, object arg1, object arg2);

public override void Write (string format, params object[] arg);

Write方法提供了很多個重載版本,但是我們只需要關注這三個即可。第一個很簡單,直接寫一個字符串。如果把第二個方法和第三個方法結合起來,然後再聯繫一下String.Format我想很多小夥伴就知道怎麼使用了。沒錯,這兩個方法的效果就是下面這種方式:

var value = string.Format(string format, params object[] arg);

writer.Write(value);

public override void WriteLine (string value);

public override void WriteLine (string format, object arg0, object arg1, object arg2);

public override void WriteLine (string format, params object[] arg);

同時C#也添加了一組WriteLine的方法,該方法與Write不同的是,WriteLine會在寫入數據後向流裡追加一個換行符,所以這個方法是寫入一行。

不過,在使用Writer的時候需要注意以下這三個方法:

public override void Flush ();

public override void Close ();

protected override void Dispose (bool disposing);

其中Dispose(銷毀)是受保護的方法,一般場景中遇不到。Flush表示將Writer的數據推送到基礎流裡,Close表示關閉Writer順便關閉基礎流。

在C#中,對Close動作進行了進一步優化。當調用Close方法的時候,系統會自動調用Flush方法將數據推送到基礎流中。那麼,為什麼還提供了Flush呢?因為如果要操作一個大數據或者數據的來源是分批,這時候為了保證之前的數據不會丟失就需要我們手動調用Flush把數據推送給基礎流了。

嗯,所以我們來寫個程序驗證一下:

class Program

{

static void Main(string[] args)

{

var reader = new StreamReader("Program.cs");

var writer = new StreamWriter("Program.cs.txt");

while(true)

{

var str = reader.ReadLine();

if(str == null)

{

break;

}

Console.WriteLine(str);

writer.WriteLine(str);

}

//writer.Close();

reader.Close();

}

}

如示例,在注釋了 writer.Close(); 之後,程序依然會生成一個Program.cs.txt 文件,但文件是空的。這時候取消注釋,就會發現Program已經複製到了Program.cs.txt裡。

3. 常用的有哪些適配器流

1. BinaryReader

用特定的編碼將基元數據類型讀作二進位值

2. BinaryWriter

將二進位中的基元類型寫入流並支持用特定的編碼寫入字符串

3.StringReader

從字符串中讀取字符串

4.StringWriter

將信息寫入字符串中

5.XmlReader/XmlWriter

對xml文件的快速操作

這幾個是出鏡率較高的,但仍有很多選手藏在幕後,並非是它們不出鏡,而是它們經常活躍在特定的領域裡。所以這裡就沒有做過多的介紹。

4. 後言

到這裡,IO流基礎知識介紹完畢。C#基礎知識系列,也只剩下《異常篇》、《實戰準備篇》以及《C#基礎實戰篇-文件檢索工具》這三大篇章了。C#系列的下一個篇章就是數據訪問系列,會介紹AOD.NET、Entity Framework等數據訪問框架。

附:

上文中提到的System.Text.Encoding是一種文本編碼類,表示字符串的編碼格式。常用的有 UTF-8,GBK2312等。其中C#在Encoding類添加了幾大常用編碼格式的靜態屬性,返回的是Encoding實例。

public static System.Text.Encoding UTF8 { get; }

public static System.Text.Encoding ASCII { get; }

相關焦點

  • C Sharp 基礎知識系列- 16 開發工具篇
    前言這是C# 基礎知識系列的最後一個內容講解篇,下一篇是基礎知識-實戰篇。這一篇主要講解一下C#程序的結構和主要編程工具。1. 工具工欲善其事必先利其器,在實際動手之前我們先來看看想要編寫一套C#程序需要做哪些準備吧。
  • 夯實Java基礎系列16:一文讀懂Java IO流和常見面試題
    正如這個系列教程之前提到過的,流通常會與數據源、數據流向目的地相關聯,比如文件、網絡等等。流和數組不一樣,不能通過索引讀寫數據。在流中,你也不能像數組那樣前後移動讀取數據,除非使用RandomAccessFile 處理文件。流僅僅只是一個連續的數據流。
  • Java【IO系列】基礎篇—6. FileInputStream和FileOutputStream詳解
    FileInputStream 和 FileOutputStream 介紹FileInputStream 是文件輸入流,它繼承於InputStream。通常,我們使用FileInputStream從某個文件中獲得輸入字節。FileOutputStream 是文件輸出流,它繼承於OutputStream。
  • CSharp 基礎知識系列- 8 Linq最後一部分查詢表達式語法實踐
    一直提的Linq查詢方式分為兩種,一種就是方法鏈的形式,官方的稱呼是流式查詢;另一種是類似於SQL語句的查詢方式,我之前叫做類SQL查詢方式,不過有的文檔稱之為查詢表達式。 注意,本篇內容需要先看過 《C# 基礎系列-7》,並有一定的對象和集合的基礎。
  • Java【IO系列】基礎篇—1. IO框架
    最近,抽空整理以前所學的java基礎。整理也就是重新學習的過程,在這個學習過程中,我一直嘗試著站在一個更高的角度來看問題,將這些類和接口的原理和思想理解的更加透徹一些!1. java io簡介java io系統的設計初衷,就是為了實現「文件、控制臺、網絡設備」這些io設置的通信。例如,對於一個文件,我們可以打開文件,然後進行讀取和寫入。
  • CSharp 基礎知識系列- 3 集合數組
    1.1 Array 數組數組,集合的基礎部分,主要特點是一經初始化就無法再次對數組本身進行增刪元素。C#雖然添加了一些修改數組的擴展方法,但基本都會返回新的數組對象。List列表通過元素數量實現了Add和Remove的操作,列表對象操作引發元素數量變動時都會導致對容量的重新計算,如果現有容量不滿足後續操作需要的話,將會對現有數組進行擴充。
  • C Sharp 基礎知識系列- 2 字符串
    string的構造函數string的構造函數一共有以下幾種:public String(char c, int count);public String(char[] value);public String(char[] value, int startIndex, int length);其他第一個: 生成數量為count的由c組成的字符串,不包括其他字符
  • Java【IO系列】基礎篇—9. DataInputStream和DataOutputStream
    代碼如下:// 處理完輸入流中單字節的符號之後,接下來我們繼續處理。while (count < utflen) { // 下面語句執行了2步操作。 // (01) 將字節由 「byte類型」 轉換成 「int類型」。
  • CSharp 數據操作系列 - 2. ADO.NET操作
    0.前言在上一篇中初略的介紹了一下SQL的基本寫法,這一篇開始我們正式步入C#操作資料庫的範圍。通過這一系列的內容,我想大家能對於資料庫交互有了一定的認識和基礎。閒話不多說,先給大家介紹一個C#操作資料庫的方式。1.
  • 「無伺服器架構」動手操作Knative -第2部分
    現在我們已經了解了Knative三項賽的基礎知識,讓我們來看一個具體的例子。Hello World事件對於Hello World事件,讓我們讀取來自谷歌雲發布/訂閱的消息並在Knative服務中註銷它們。
  • Java【IO系列】基礎篇—15. BufferedReader和BufferedWriter詳解
    示例代碼關於BufferedReader中API的詳細用法,參考示例代碼(BufferedReaderTest.java):import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.File;import java.io.InputStream;
  • Java【IO系列】基礎篇—16. PrintWriter和RandomAccessFile詳解
    它不包含用於寫入原始字節的方法,對於這些字節,程序應該使用未編碼的字節流進行寫入。FileInputStream 只能對文件進行讀操作,而FileOutputStream 只能對文件進行寫操作;但是,RandomAccessFile 同時支持文件的讀和寫,並且它支持隨機訪問。
  • C Sharp 基礎知識系列 - 15 異常處理
    我來舉幾個例子:程序需要訪問一個文件,但這個文件不存在,當程序嘗試打開一個讀該文件的流時就會出錯成績管理系統中,成績需要一個浮點型的數字,但是輸入的人錯誤的輸入了其他符號或者用中文輸入了成績程序需要通過網絡與其他伺服器進行交互
  • 玩轉Mysql系列 - 第6篇:select查詢基礎篇
    這是Mysql系列第6篇。環境:mysql5.7.25,cmd命令中進行演示。DQL(Data QueryLanguage):數據查詢語言,通俗點講就是從資料庫獲取數據的,按照DQL的語法給資料庫發送一條指令,資料庫將按需求返回數據。DQL分多篇來說,本文屬於第1篇。
  • C#學習路線(看完不惑系列)
    Visual Studio 2017下載路徑:https://visualstudio.microsoft.com/zh-hans/downloads/本篇文章剩餘部分主要是介紹利用微軟C#官方文檔和網上資源學習。
  • C Sharp 數據操作系列 - 1. SQL基礎操作
    這一篇我們將繼續為C#數據操作的基礎填上一個空白-SQL語句。SQL(Structured Query Language,結構化查詢語言)是一種特定的程式語言,用於管理資料庫系統,操作數據甚至編寫一些程序。
  • 在程序中如何正確地創建和銷毀軟體應用系統中文件IO流對象實例
    軟體項目實訓及課程設計指導——如何正確地創建和銷毀軟體應用系統中文件IO流對象實例1、Java文件輸入輸出(讀寫)相關的技術基礎知識(1)流(Stream)它是通過緩衝機制將數據從生產者(如鍵盤、磁碟文件、內存或其他設備
  • PIC單片機CCS之C語言(#USE FAST_IO的用法)
    #USE FAST_IO語法:#use fast_io(port)本文引用地址:http://www.eepw.com.cn/article
  • 35-python高級篇-C10K問題和io多路復用
    我們通過上一期的文章34-python高級篇-並發,並行,同步,異步,阻塞和非阻塞是什麼?學習了協程中幾個專業名詞的含義。今天我們將認識下C10K問題和io多路復用的知識。    解決C10K問題,將用到上一期推文中所介紹的那幾個知識,同時也將使用到本期推文中將要介紹到的io多路復用技術,接下來,讓我們一起來看看如上的技術挑戰在解決方法上對應的知識點吧。io多路復用    在介紹io多路復用之前,先跟小潭認識下unix下的五種I/O模型。
  • 「GCTT 出品」對 Go 中長時間運行 io.Reader 和 io.Writer 的操作...
    本文很長,不想深究瞅這裡:這篇文章最終導向 progress 包,你可以在自己的項目中自由使用——https://github.com/machinebox/progress考慮到 io.Reader 和 io.Writer 都是接口,我們可以封裝它們並且攔截 Read 和 Write 方法,捕獲實際已經通過它們的字節數