Java---IO 流

導語:

打開簡書,看到自己的文章被瀏覽了五十多次的時候真的很開心,然后發現有幾個喜歡一個粉絲的時候,真的是非常開心,同時謝謝你們給了我繼續寫下去的動力,非常感謝。這篇文章寫一寫關于IO 流中自己學到的一些基本知識,以及遇到的問題,希望能夠幫助到大家。

1.IO 流的引入

我們可以利用 File 類將 java 程序跟硬盤上的文件聯系起來。我們可以獲取其中某些屬性。但是,文件的內容我們不能操作,讀取。這時候就引入了 對文件進行操作的 IO 流。

IO 流示意圖.png

我們可以生動形象的把 IO 流當作一根根管子,管子的一端懟到目標文件,另一端懟到程序中。我們寫的程序在這里相當于一個數據傳輸的中轉站。

2. IO 流分類

2.1 按照讀取單位劃分:

字節流 字符流
輸入流 InputStream Reader
輸出流 OutputStream Writer

2.2 按照功能劃分

  • 節點流:直接從源文件讀取數據到程序中 —— 一根管。
  • 處理流:需要多個流結合使用 —— 多根管。

在我目前所看到的所有程序中,想要利用 IO 流,必須先用輸入輸出字節流,或者輸入輸出字符流連接到目標文件?。?!

下面主要寫一下各個流的用法。如有疑問以及不妥的地方歡迎指正,感激不盡!

3. 字節流--FileInputStream,FileOutputStream

3.1 文件 ---> 程序(以程序為主體,對于程序來說屬于對內輸入,所以要用輸入流)

3.1.1 利用單個字節

1  public class Test {
2    public static void main(String[] args) throws IOException {
3       //1.確定文件:
4       File f=new File("i:/test/haha.txt");
5       //2.創建一根管,然后連接文件:
6       FileInputStream fis=new FileInputStream(f);
7       //3.進行動作: 吸    (流:讀取)
8       int n=fis.read();
9       while(n!=-1){//讀到文件末尾就是-1
10          System.out.print(n+"\t");
11          n=fis.read();
12      }
13      //4.關閉流(無論什么時候,關閉流是必須的):
14      fis.close();
15  }
16 }

首先要確定被讀取文件的地址(代碼第 4 行),然后創建一根管(就是 IO 流),一端懟到該文件上去,另一端懟到程序中,進行吸的動作(也就是程序讀取文件)。(怎么樣,理解的還可以不,哈哈)

從上面的代碼第 8 行和第 11 行可以看出 這種方法是一個字節一個字節的將文件中的信息讀入到程序中。

缺點:

  • 運行結果會出現亂碼;
  • 一個字節一個字節讀取 ,效率太低;

3.1.2 利用數組緩沖區

1.  public class Test02 {
2.   public static void main(String[] args) throws IOException {
3.       //創建文件:
4.       File f=new File("i:/test/haha.txt");
5.       //創建一根管,然后連接文件:
6.       FileInputStream fis=new FileInputStream(f);
7.       //進行動作: 吸    (流:讀取)
8.       byte[] b=new byte[8];// 隨便創建了一個byte數組,長度為8
9.       int len=fis.read(b);// len是數組中被占用的長度
10.      while(len!=-1){// 讀到文件末尾就是-1
11.          System.out.print(len+"\t");
12.          len=fis.read(b);
13.      }
14.      //關閉流:
15.      fis.close();
16.  }
}

這種方法是定義了一個數組(第 8 行)8個字節,通俗一點說,就是每 8 個字節為一組進行讀取,用數組將文件中的信息讀入到程序中,效率比比上一種方法高。

3.2 程序 ---> 文件(以程序為主體,對于程序來說屬于對外輸出的,所以要用輸出流)

3.2.1 利用單個字節

1  public class Test03 {
2    public static void main(String[] args) throws IOException {
3        //創建文件:
4        File f=new File("i:/test/haha.txt");
5        //創建一根管,然后連接文件:
6        FileOutputStream fos=new FileOutputStream(f);
7        //進行動作: 吐    (流:寫入)
8        String str="abc123你好";
9        //將字符串轉化成字節:
10      byte[] bytes=str.getBytes();
11      for (int i = 0; i < b.length; i++) {
12         fos.write(b[i]);
13      }
14      //關閉流:
15      fos.close();
16   }
17  }

首先要確定要把文件讀取到哪兒(代碼第 4 行),然后創建一根管(就是 IO 輸出流),一端懟到該文件上去,另一端懟到程序中,進行吐的動作(也就是把內容從程序中寫出去)。因為字節輸出流只能一個字節一個字節的向外讀取,所以要調用 String 的 getBytes() 方法將 String 類型數據準換成 byte 類型,而該方法返回值為一個 byte 類型數組。

3.2.2 利用緩沖數組

1 public class Test04 {
2   public static void main(String[] args) throws IOException {
3        //創建文件:
4        File f=new File("i:/test/haha.txt");
5        //創建一根管,然后連接文件:
6        FileOutputStream fos=new FileOutputStream(f);
7        //進行動作: 吐    (流:寫入)
8        String str="abc123你好"; 
9        byte[] bytes=str.getBytes();//將字符串轉化成字節:因為fos只能一個字節一個字節的寫
10      for(byte b:bytes){
11          fos.write(b);
12      }
13      //關閉流:
14      fos.close();
15  }
16 }

定義了一個數組(第 9 行),調用 String 類型的 getBytes() 方法,將字符串轉化為字節,用一個數組接住該方法的返回值。然后再用循環遍歷將數組中的信息讀取到文件中,效率比比上一種方法高。

一定要記得關流?。?!

3.3 文件的復制

3.3.1 利用單個字節進行復制

1 public class TestCopyDoc {
2   public static void main(String[] args) throws IOException {
3       //先確定文件:
4       File f1=new File("i:/test/haha.txt");
5        File f2=new File("i:/test/demo.txt");
6       //創建兩個輸入輸出流(兩個管):
7       FileInputStream fis=new FileInputStream(f1);//輸入流
8       FileOutputStream fos=new FileOutputStream(f2);//輸出流
9       //開始動作: 吸-----吐
10     int n=fis.read();//吸;
11     while(n!=-1){
12          fos.write(n);//吐;
13          n=fis.read();//吸;
14      }   
15      //關閉流:
16      fis.close();
17      fos.close();
18  }
19 }

功能:就是利用輸入輸出字節流,一個字節一個字節的將 i:/test/haha.txt 文件中的內容復制到 i:/test/demo.txt 中去。

程序相當于一個中轉站(開篇的 IO 流示意圖),利用字節輸入流(FileInputStream)將 haha.txt 中的內容讀取到程序中,然后利用字節輸出流(FileOutputStream)從程序中寫出到目標文件 demo.txt.

3.3.2 利用數組緩沖區

1 public class TestCopyDoc02 {
2   public static void main(String[] args) throws IOException {
3       //先確定文件:
4       File f1=new File("i:/test/haha.txt");
5       File f2=new File("i:/test/demo.txt");
6       //創建兩個輸入輸出流(兩個管):
7       FileInputStream fis=new FileInputStream(f1);//輸入流
8       FileOutputStream fos=new FileOutputStream(f2);//輸出流
9       //開始動作: 吸-----吐
10      byte[] b=new byte[8];
11      int len=fis.read(b);//len---是這個數組中被占用的數量
12      while(len!=-1){
13          fos.write(b,0,len);
14          len=fis.read(b);
15      }
16      //關閉流:
17      fis.close();
18      fos.close();
19  }
20 }

功能:定義了一個數組(第 10 行)8個字節,每 8 個字節為一組進行讀取,FileInputStream 利用數組將 haha.txt 中的信息讀入到程序中(第 11 行),其中 len 表示這個數組中被占用的數量,當讀取到文件結尾的時候 len = -1(這是規定,我也不知道為啥是 -1 ,不是其他的數字,這個記住就好。。。);然后 FileOutputStream 把程序中讀取到的信息寫入到目標文件中(第 14 行)。

4. 字符流--FileReader,FileWriter

  • 和字節流一樣,用字符流進行文件的復制也分為兩種方法,一種是利用單個字符進行讀取和寫出,另一種是利用數組進行讀取和寫出

4.1 利用單個字符

1 public class TestFileCopy001 {
2   public static void main(String[] args) throws IOException {
3       //1.創建目標文件,源文件
4       File f1=new File("i:/test/haha.txt");
5       File f2=new File("i:/test/demo.txt");
6       //2.創建字符流
7       FileReader fr=new FileReader(f1);
8       FileWriter fw=new FileWriter(f2);
9       //3.讀取
10     int n=fr.read();
11     while(n!=-1){
12         fw.write(n);
13         n=fr.read();
14     }
15     //4.關閉流
16     fw.close; 
17       fr.close;
18  }
19 }
  • 功能:和 3.3.1 中一樣,只不過是利用字符流來進行操作。

4.2 利用數組緩沖區---char[]

1 public class TestFileCopy001 {
2   public static void main(String[] args) throws IOException {
3       //1.創建目標文件,源文件
4       File f1=new File("i:/test/haha.txt");
5       File f2=new File("i:/test/demo.txt");
6       //2.創建字符流
7       FileReader fr=new FileReader(f1);
8       FileWriter fw=new FileWriter(f2);       
9       //3.讀取
10      char[] ch=new char[8];
11      int len=fr.read(ch); //len---是這個數組中被占用的數量
12      while(len!=-1){
13          fw.write(ch,0,len);
14          len=fr.read(ch);
15      }
16      //4.關閉流
17      fw.close;
18        fr.close;
19  }   
20 }
  • 功能:和 3.3.2 類似,只不過是定義了一個字符數組(代碼第 10 行)進行讀取和寫出操作。

另外要說明的是:用字符流復制非純文本的文件都是不行的,都是耍流氓!因為用字符流復制的時候,它會按照系統的字符碼表進行查找和替換,把二進制數據全部按照碼表替換了,但是圖片的一些代碼能在碼表中找到相對應的編碼,就轉換成編碼,另外還有一些找不到,JVM就會用類似的編碼代替,那么你再打開的時候就肯定不是圖片了。

總之,記住千萬不要耍流氓啊!!

5. 緩沖字節流--BufferedInputStream,BufferedOutputStream

先上圖(以下圖都是按照個人理解畫出來的,如有錯誤還請指正):


字節流復制文件原理

我們上面寫的代碼都是利用字節流進行文件的復制。以上圖為例,每次讀取或寫出都會對硬盤上的文件訪問一次,缺點就是對硬盤的訪問次數太多,對硬盤來說這是有害的。這時,就可以利用緩沖字節流。

再上圖:

緩沖字節流復制文件原理

根據下面的代碼來理解一下這張圖:

1 public class Test {
2   public static void main(String[] args) throws IOException {
3       //1.源文件,目標文件
4       File f1=new File("i:/test/haha.txt");
5       File f2=new File("i:/test/demo.txt");
6       //2.創建流:4個
7       FileInputStream fis =new FileInputStream(f1);
8       FileOutputStream fos=new FileOutputStream(f2);
9       BufferedInputStream bis=new BufferedInputStream(fis);//創建緩沖流bis
10      BufferedOutputStream bos=new BufferedOutputStream(fos);//創建緩沖流bos
11      //3.讀取
12      byte[] b=new byte[8];
13      int len=bis.read(b);//bis 讀取數據
14      while(len!=-1){
15          bos.write(b,0,len);//bos 寫出數據
16          len=bis.read(b);
17      }
18      //4.關閉流:
19      bis.close();
20      bos.close();
21      fis.close();
22      fos.close();    
23  }
24 }

程序解釋:先創建輸入字節流 fis (第 7 行)懟到目標文件(i:/test/haha.txt),然后創建緩沖字節流 bis(如圖紅色),bis 套在 fis 上使用(相當于一根管子上套了另一根管子)。使用緩沖流會有一個緩沖區,fis(字節輸入流)會盡可能多的把源文件中的數據讀取到緩沖區,然后 bis 再利用緩沖數組從緩沖區中每 8 個字節為一組的讀取。寫出的過程和讀取的過程正好相反就不在此贅述啦。(如果還不理解可以留下評論)

這種方式屬于一根流套在另一根流,也就是管套管。上使用這樣的話就減少了對硬盤的訪問次數。

6. 緩沖字符流--BufferedReader,BufferedWriter

緩沖字符流和上面的緩沖字節流的運行原理一樣,只不過一個使用字節流,一個使用字符流而已。

1 public class Test {
2   public static void main(String[] args) throws IOException {
3       //1.源文件,目標文件
4       File f1=new File("i:/test/haha.txt");
5       File f2=new File("i:/test/demo.txt");
6       //2.創建流:4個
7       FileReader fis =new FileReader(f1);
8       FileWriter fos=new FileWriter(f2);
9       BufferedReader bis=new BufferedReader(fis);//創建緩沖字符輸入流 bis
10      BufferedWriter bos=new BufferedWriter(fos);//創建緩沖字符輸出流 bos
11      //3.讀取
12      char[] b=new char[8];
13      int len=bis.read(b);//bis 讀取數據
14      while(len!=-1){
15          bos.write(b,0,len);//bos 寫出數據
16          len=bis.read(b);
17      }
18      //4.關閉--先關高級流,再關閉低級流
19      bis.close();
20      bos.close();
21      fis.close();
22      fos.close();    
23  }
24 }

這種方式也是屬于管套管來操作數據,效率比上面的緩沖字節流要高(因為使用的是字符流)。

之前操作數據,要么是一個字節一個字節的讀取,或者是一個數組一個數組的讀取。那么下面介紹一種效率更高的方式:一整行一整行的讀取數據。

1        //3.讀取
2       String str=bis.readLine();//利用緩沖字符輸入流 bis 整行整行的讀取數據;
3       while(str!=null){
4           bos.write(str);
5           bos.newLine();//在目標文件中換行
6           str=bis.readLine();//利用緩沖字符輸出流 bos 整行整行的寫出數據;
7       }

程序其他部分不變,只是讀取的方式不一樣。

7. System對 IO 的支持

在這里補充一下,我們寫程序的時候經常會用到鍵盤輸入這個語句:Scanner sc=new Scanner(System.in);
那么我有沒有考慮過鍵盤輸入這件事到底是誰來完成的呢,是 Scanner 還是 Sytem.in ?
其實,鍵盤錄入這個功能是由 System.in 來完成的,Scanner 只是起到一個掃描器的作用。System.in 會返回一個 InputStream 類型的變量,也就是返回一個流。那么Scanner sc=new Scanner(System.in);這條語句可以通俗的理解為有一個掃描器 Scanner,一個鍵盤,它們倆之間是用一根管子(流)連接起來,鍵盤錄入的數據通過這根管子傳進掃描器中。
還有一個比較坑的地方,寫出來給大家做個提醒:

1 public class TestIO {
2   public static void main(String[] args) throws IOException {
3       InputStream in = System.in; 
4       byte[] b=new byte[8];
5       int n = in.read(b);//用數組讀取鍵盤輸入的數據,返回結果為 b 被占用的長度
6       System.out.print(n);
7   }
8 }

代碼第 5 行中,in.read(b) 按理說返回結果為 b 被占用的長度啊,那么,我輸入數字 1,結果輸出 3,也就是說,占用了 3 個字節!哇,當時整的我很郁悶,不就占用了一個字節嗎,應該是 1 才對嘛。
后來才搞清楚,運行這段代碼的時候,你輸入 1 之后,按下了 Enter 鍵,也就是輸入完數據之后進行了回車、換行,而回車和換行在 ASCII 表中對應的數值分別是 13 和 10,它們倆又各占了一個字節,所以是占用了 3 個字節,輸出結果為 3。不知道我有沒有表達清楚?

  • 補充一點:字節流轉化為字符流
1 public class Test {
2   public static void main(String[] args) throws IOException {
3       //輸入:
4       InputStream in = System.in;
5       //轉換流--單向轉換:字節流-->字符流轉換
6       InputStreamReader isr=new InputStreamReader(in);
7       BufferedReader br=new BufferedReader(isr);
8       //輸出:
9       FileWriter fw=new FileWriter("d:/bjsxt/t.txt");
10      BufferedWriter bw=new BufferedWriter(fw);
11      //讀取:
12      String str=br.readLine();
13      while(!str.equals("byebye")){
14          bw.write(str);
15          bw.newLine();
16          str=br.readLine();
17      }
18      //關閉流:
19      bw.close();
20      br.close();
21      fw.close();
22      isr.close();
23      in.close(); 
24  }
25 }

這段代碼中,主要想表達的就是第 6 行,將字節流轉化為字符流,其他的就是和之前的代碼差不多,都是讀取寫入。

8. 數據流--對基本數據類型處理--DataInputStream,DataOutputStream

8.1 將基本數據類型的東西輸入到目標文件中去:

1 public class Test001 {
2   public static void main(String[] args) throws IOException {
3       DataOutputStream dos=new DataOutputStream(new 4FileOutputStream(new File("d:/haha/demo001.txt")));//將流套在一起使用
5       dos.writeInt(12);//int
6       dos.writeChar('\n');
7       dos.writeDouble(12.0);//double
8       dos.writeChar('\n');
9       dos.writeUTF("hellojava你好");//
10      //關閉流
11      dos.close();    
12  }
13 }

代碼第 3 行是將各個需要的流套在一起直接一句代碼寫出來了,應該能看懂吧。
執行完這段程序之后如果打開目標文件,將會看到...亂碼,哈哈。別慌,因為這是給程序看的,咱可能看不懂的。那么要是想看該怎么辦呢?有辦法,在一個程序中執行下段代碼:

1       DataInputStream dis=new DataInputStream(new FileInputStream(new File("d:/bjsxt/demo001.txt")));
3       System.out.println(dis.readInt());//int
4       System.out.println(dis.readChar());//char
5       System.out.println(dis.readDouble());//double
6       System.out.println(dis.readChar());//char
7       System.out.println(dis.readUTF());  
8       dis.close();//關閉流

這段代碼是為了將文件中的內容寫入到程序中,然后在控制臺輸出。第一句不用解釋了吧。而且,重要的是寫進文件的順序和讀到程序中的順序必須一致!就是說,假如你寫進文件的第一個是 int 類型的,那么輸出的第一個也必須是 int 型的,這樣一一對應才能保證不出錯。

9. 對象流--對引用數據類型處理--ObjectInputStream,ObjectOutputStream

9.1 放入String 類型數據:

1 public class Test002 {
2   public static void main(String[] args) throws FileNotFoundException, IOException {
3     ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(new File("d:/haha/t.txt")));
4     oos.writeObject("java");
5     oos.close();//關閉流 
6   }
7 }

這段代碼就是將 “java” 寫到目標文件中去。

9.3 現在要寫入 Person 的一個對象:

假如說我自定義了一個 Person 類。,然后我現在要把一個 Person 類型的對象放進目標文件中去,按照上面的放 String 類型數據的方法:

1 public class Test002 {
2   public static void main(String[] args) throws FileNotFoundException, IOException {
3       ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(new File("d:/bjsxt/t.txt")));
4       oos.writeObject(new Person("lili", 18));
5       oos.close();    
6    }
7 }

這段程序運行結果如下:

運行結果

發現出錯了,“NotSerializableExeption”,啥意思呢,就是說** Person 類沒有序列化!!**
那么怎么解決呢?方法:實現 Serializable 方法,加序列號。

調試方法

謹記:以后如果要對類的對象進行網絡傳輸一定要實現序列化?。?!


如果您能看到這里,我真的表示萬分感謝,這篇文章如果有什么錯誤的地方,或者您不理解的地方,歡迎留言。另外我這兒有這部分學習的視頻,如果需要的話,也可以留下郵箱,我發給您。就這樣,謝謝各位!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,791評論 6 545
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,795評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,943評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,057評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,773評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,106評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,082評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,282評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,793評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,507評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,741評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,220評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,929評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,325評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,661評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,482評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,702評論 2 380

推薦閱讀更多精彩內容

  • 概述 java.io 包幾乎包含了所有操作輸入、輸出需要的類。所有這些流類代表了輸入源和輸出目標。java.io ...
    Steven1997閱讀 9,225評論 1 25
  • tags:io categories:總結 date: 2017-03-28 22:49:50 不僅僅在JAVA領...
    行徑行閱讀 2,199評論 0 3
  • 標準輸入輸出,文件的操作,網絡上的數據流,字符串流,對象流,zip文件流等等,java中將輸入輸出抽象稱為流,就好...
    navy_legend閱讀 726評論 0 0
  • 一直有個隱居的夢。像陶淵明一樣“采菊東籬下,悠然見南山”。一直覺得生活在農村比生活在城市幸福。在農村,可以...
    森林的清味閱讀 1,226評論 0 2
  • 《三朝元老》篇 橫批:三朝元老 上聯:一二三四五六七 (亡八) 下聯:孝弟忠信禮義廉 (無恥) 《局詐》篇 “道士...
    倒映書影閱讀 468評論 0 0