Java筆記(二十八)……IO流下 IO包中其餘經常使用類以及編碼表問題

PrintWriter打印流

Writer的子類,既能夠接收字符流,也能夠接收字節流,還能夠接收文件名或者文件對象,很是方便java

同時,還能夠設置自動刷新以及保持原有格式寫入各類文本類型的print方法windows

PrintWriter的小例子:打印字符錄入的大寫

   1: //讀取鍵盤錄入,打印大寫
   2: private static void printWriterMethod() throws IOException
   3: {
   4:     BufferedReader bufr =
   5:         new BufferedReader(new InputStreamReader(System.in));
   6:  
   7:     PrintWriter out = new PrintWriter(System.out,true);
   8:  
   9:     String line = null;
  10:  
  11:     while( (line = bufr.readLine()) != null)
  12:     {
  13:         if("over".equals(line))
  14:             break;
  15:         //只需一條語句,即可打印一行數據,很是方便
  16:         out.println(line.toUpperCase());
  17:     }
  18:     
  19:     out.close();
  20:     bufr.close();
  21: }

SequenceInputStream合併流

序列流,可將多個流合併成一個流,按序列進行讀取數組

可手動指定各個流建立對象,也可將多個流存入集合,利用枚舉Enumeration來建立對象安全

合併和分割流的小例子:

   1: import java.io.*;
   2: import java.util.*;
   3:  
   4: class SequenceInputStreamDemo 
   5: {
   6:     public static void main(String[] args) throws IOException
   7:     {
   8:  
   9:         int num = splitFile(new File("pic.jpg"));
  10:  
  11:         /*
  12:         合併分割後的流
  13:         */
  14:         
  15:         //定義Vector集合存儲全部part文件的字節輸入流
  16:         Vector<FileInputStream> v = new Vector<FileInputStream>();
  17:  
  18:         for(int i = 1 ; i <= num ; i ++ )
  19:         {
  20:             v.add(new FileInputStream(i+".part"));
  21:         }
  22:  
  23:         Enumeration<FileInputStream> en = v.elements();
  24:  
  25:         FileOutputStream fos = new FileOutputStream("pic1.jpg");
  26:         
  27:         //定義序列流,經過枚舉合併全部的輸入流
  28:         SequenceInputStream sis = new SequenceInputStream(en);
  29:  
  30:         byte[] buf = new byte[1024];
  31:  
  32:         int len = -1;
  33:  
  34:         while( (len = sis.read(buf)) != -1)
  35:         {
  36:             //將合併後的流寫入一個文件
  37:             fos.write(buf,0,len);
  38:         }
  39:  
  40:         fos.close();
  41:         sis.close();    
  42:     }
  43:     
  44:     //分割流
  45:     private static int splitFile(File f) throws IOException
  46:     {
  47:         FileInputStream fis = new FileInputStream(f);
  48:  
  49:         long size = f.length();
  50:  
  51:         byte[] buf = null;
  52:         
  53:         //選擇緩衝區大小
  54:         if(size > 1024*1024*5)
  55:             buf = new byte[1024*1024];
  56:         else
  57:             buf = new byte[(int)size/5];
  58:  
  59:         int len = -1;
  60:         int count = 1;
  61:  
  62:         while( (len = fis.read(buf)) != -1)
  63:         {
  64:             //每一個緩衝區的內容分別寫入不一樣的part文件
  65:             FileOutputStream fos = new FileOutputStream((count++)+".part");
  66:             fos.write(buf,0,len);
  67:             fos.close();
  68:         }
  69:  
  70:         fis.close();
  71:  
  72:         return count-1;
  73:     }
  74: }

對象的序列化

ObjectInputStream,ObjectOutputStream多線程

將對象存取在硬盤上,叫作對象的持久化存儲(存儲的是對象的屬性值,而不是方法)app

想要對對象進行序列化,該對象必須實現Serializable接口,Serializable接口沒有方法,稱爲標記接口,實現過程只是給實現者加入一個序列化的ID:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 其實就是序列號,這個序列號是由變量的聲明語句自動生成的,咱們也能夠本身定義類的序列號dom

對象序列化的小例子

   1: import java.io.*;
   2:  
   3: class Person implements Serializable
   4: {
   5:     //序列號,保證類型一致
   6:     static final long serialVersionUID = 42L;
   7:  
   8:     //靜態變量以及transient修飾的變量不會被序列化
   9:     static String country = "cn";
  10:     transient int grade;
  11:     private String name;
  12:     private int age;
  13:  
  14:     Person(String name,int age,int grade,String country)
  15:     {
  16:         this.name = name;
  17:         this.age = age;
  18:         this.grade = grade;
  19:         this.country = country;
  20:     }
  21:  
  22:     public String toString()
  23:     {
  24:         return name+"::"+age+"::"+grade+"::"+country;
  25:     }
  26: }
  27:  
  28: class ObjectStreamDemo 
  29: {
  30:     public static void main(String[] args) throws Exception
  31:     {
  32:  
  33:         //writeObj();
  34:         readObj();
  35:     }
  36:  
  37:     //將對象寫入流中
  38:     private static void writeObj() throws IOException
  39:     {
  40:         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
  41:  
  42:         oos.writeObject(new Person("Shawn",30,3,"en"));
  43:         oos.writeObject(new Person("feng",23,6,"usa"));
  44:  
  45:         oos.close();
  46:     }
  47:     
  48:     //將對象從流中讀出並打印
  49:     private static void readObj() throws Exception
  50:     {
  51:         ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
  52:  
  53:         Person p1 = (Person)ois.readObject();
  54:         Person p2 = (Person)ois.readObject();
  55:  
  56:         System.out.println("p1 --- "+p1);
  57:         System.out.println("p2 --- "+p2);
  58:  
  59:         ois.close();
  60:     }
  61: }

io8

咱們能夠看到,靜態變量和transient修飾的變量是不會被序列化到硬盤上的this

管道流

PipedInputStream,PipedOutputStream編碼

管道Demo,一個線程寫,一個線程讀

   1: import java.io.*;
   2:  
   3: //讀管道流線程
   4: class Read implements Runnable
   5: {
   6:     private PipedInputStream pis;
   7:  
   8:     Read(PipedInputStream pis)
   9:     {
  10:         this.pis = pis;
  11:     }
  12:  
  13:     public void run()
  14:     {
  15:         try
  16:         {
  17:             byte[] buf = new byte[1024];
  18:         
  19:             int len = -1;
  20:             
  21:             //阻塞方法,讀不到數據會等待
  22:             len = pis.read(buf);
  23:  
  24:             System.out.println(new String(buf,0,len));
  25:  
  26:             pis.close();
  27:         }
  28:         catch (IOException e)
  29:         {
  30:             System.out.println("pipe read error!");
  31:         }
  32:         
  33:     }
  34: }
  35:  
  36: //寫管道流線程
  37: class Write implements Runnable
  38: {
  39:     private PipedOutputStream pos;
  40:  
  41:     Write(PipedOutputStream pos)
  42:     {
  43:         this.pos = pos;
  44:     }
  45:  
  46:     public void run()
  47:     {
  48:         try
  49:         {
  50:             pos.write("pipe is coming!".getBytes());
  51:  
  52:             pos.close();
  53:         }
  54:         catch (IOException e)
  55:         {
  56:             System.out.println("pipe write error!");
  57:         }
  58:  
  59:     }
  60: }
  61:  
  62: class PipedStreamDemo 
  63: {
  64:     public static void main(String[] args) throws IOException
  65:     {
  66:         PipedInputStream pis = new PipedInputStream();
  67:         PipedOutputStream pos = new PipedOutputStream();
  68:         
  69:         //連接讀寫管道
  70:         pis.connect(pos);
  71:  
  72:         new Thread(new Read(pis)).start();
  73:  
  74:         new Thread(new Write(pos)).start();
  75:     }
  76: }

隨機訪問文件流

RandomAccessFilespa

直接繼承Object類,內部封裝了字節輸入輸出流,同時封裝了文件的指針,可對基本數據類型進行直接讀寫,最大的好處是能夠實現數據的分段寫入,經過seek方法。

用隨機訪問實現的多線程複製文件(後期會改進代碼,完成多線程下載)

   1: import java.io.*;
   2:  
   3: //下載線程
   4: class DownLoadThread implements Runnable
   5: {
   6:     private RandomAccessFile in;
   7:     private RandomAccessFile out;
   8:     private int offset;//偏移量
   9:     private int buf_size;//分配數據量
  10:     private int block_size;//緩衝區大小
  11:  
  12:     //初始化
  13:     DownLoadThread(RandomAccessFile in,RandomAccessFile out,int offset,int buf_size)
  14:     {
  15:         this.in = in;
  16:         this.out = out;
  17:         this.offset = offset;
  18:         this.buf_size = buf_size;
  19:         
  20:         block_size = 1024*512;
  21:         if(buf_size < block_size)
  22:             block_size = buf_size;
  23:  
  24:     }
  25:  
  26:     public void run()
  27:     {
  28:         try
  29:         {        
  30:             System.out.println(Thread.currentThread().getName()+"開始下載...");
  31:             
  32:             //讀寫流都偏移到指定位置
  33:             in.seek(offset);
  34:             out.seek(offset);
  35:  
  36:             byte[] buf = new byte[block_size];
  37:                 
  38:             int len = -1;
  39:  
  40:             int lastSize = buf_size;
  41:             
  42:             //讀取信息並寫入到目的地
  43:             while( (len = in.read(buf)) != -1)
  44:             {
  45:                 out.write(buf,0,len);
  46:  
  47:                 lastSize -= len;
  48:                 
  49:                 //分配數據量完成,結束線程
  50:                 if(lastSize == 0)
  51:                     break;
  52:                 if(lastSize < block_size)
  53:                 {
  54:                     block_size = lastSize;
  55:                     buf = new byte[block_size];
  56:                 }
  57:             }
  58:             
  59:             System.out.println(Thread.currentThread().getName()+"下載完成!");
  60:  
  61:             in.close();
  62:             out.close();
  63:             
  64:         }
  65:         catch (IOException e)
  66:         {
  67:             throw new RuntimeException(e);
  68:         }
  69:         
  70:     }
  71: }
  72:  
  73: class MutiDownLoadDemo 
  74: {
  75:     public static void main(String[] args) throws Exception
  76:     {
  77:         //肯定源文件和目的文件
  78:         File fin = new File("1.avi");
  79:         File fout = new File("5.avi");
  80:         
  81:  
  82:         multiDownload(fin,fout,12);
  83:  
  84:  
  85:     }
  86:     
  87:     //多線程下載 thread_num爲線程數
  88:     private static void multiDownload(File fin,File fout,int thread_num) throws Exception
  89:     {
  90:         RandomAccessFile in = new RandomAccessFile(fin,"r");
  91:  
  92:         RandomAccessFile out = new RandomAccessFile(fout,"rwd");
  93:  
  94:         int len = (int)fin.length();
  95:         
  96:         //肯定目的文件大小
  97:         out.setLength(len);
  98:         
  99:         in.close();
 100:         out.close();
 101:  
 102:         System.out.println("-----------File size : "+(len>>20)+" MB--------------");
 103:         System.out.println("-----------Thread num: "+thread_num+"---------");
 104:         
 105:         //肯定每一個線程分配的數據量
 106:         int buf_size = len/thread_num;
 107:  
 108:         System.out.println("-----------buffer size: "+(buf_size>>20)+" MB-----------");
 109:  
 110:         //開啓每一個線程
 111:         for(int i = 0 ; i < thread_num ; i ++)
 112:         {
 113:             //"rwd"模式表明可讀可寫而且線程安全
 114:             new Thread(
 115:                 new DownLoadThread(new RandomAccessFile(fin,"r"),new RandomAccessFile(fout,"rwd"),i*buf_size,buf_size)
 116:                 ).start();
 117:         }
 118:     }
 119: }

基本數據類型流對象

DataInputStream,DataOutputStream

   1: public static void main(String[] args) throws IOException
   2: {
   3:     DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
   4:  
   5:     dos.writeInt(123);
   6:  
   7:     dos.writeDouble(123.45);
   8:  
   9:     dos.writeBoolean(true);
  10:  
  11:     DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
  12:  
  13:     System.out.println(dis.readInt());
  14:     System.out.println(dis.readDouble());
  15:     System.out.println(dis.readBoolean());
  16: }

內存做爲源和目的的流對象

操做字節數組

ByteArrayInputStreamByteArrayOutputStream

操做字符數組

CharArrayReaderCharArrayWrite

操做字符串

StringReaderStringWriter

字符編碼

字符流出現是爲了更方便的操做字符,經過InputStreamReaderOutputStreamWriter能夠任意指定編碼表進行解碼轉換

編碼表

計算機開始只能識別二進制數據,爲了更方便的表示各個國家的文字,就將各個國家的文字與二進制數據進行一一對應,造成了一張表,即爲編碼表

常見的編碼表

ASCII:美國標準信息交換碼。

用一個字節的7位能夠表示。

ISO8859-1:拉丁碼錶。歐洲碼錶

用一個字節的8位表示。

GB2312:中國的中文編碼表。

GBK:中國的中文編碼表升級,融合了更多的中文文字符號。

Unicode:國際標準碼,融合了多種文字。

全部文字都用兩個字節來表示,Java語言使用的就是unicode

UTF-8:最多用三個字節來表示一個字符

......

編碼規則

只有GBK和UTF-8識別中文,GBK向下兼容GB2312

GBK兩個字節表示一個字符,UTF-8是1-3個字節表示一個字符,每一個字節前面1-3位做爲標識頭

GBK和UTF-8都兼容ASCII碼錶

模擬編解碼過程代碼

   1: public static void main(String[] args) throws Exception
   2: {
   3:     //字符串
   4:     String s = "你好";
   5:     
   6:     //用UTF-8編碼表編碼s字符串
   7:     byte[] b = s.getBytes("UTF-8");
   8:     
   9:  
  10:     System.out.println(Arrays.toString(b));
  11:     
  12:     //用GBK編碼表解碼
  13:     String s1 = new String(b,"GBK");
  14:     
  15:     //獲取以後發現不是原來的字符串
  16:     System.out.println(s1);
  17:     
  18:     //用GBK從新編碼回去
  19:     byte[] b1 = s1.getBytes("GBK");
  20:     
  21:     //再用UTF-8解碼
  22:     String s2 = new String(b1,"UTF-8");
  23:  
  24:     System.out.println(s2);
  25:     
  26:     
  27: }

這樣作存在一個問題,因爲GBK與UTF-8都支持中文,因此UTF-8編解碼時有可能會去內部的類似碼錶去查找,這樣編碼出來的字符就會與原字符不符,因此通常使用ISO8859-1與中文碼錶互相編解碼轉換

一個有趣的小例子

新建一個文本文檔,寫入「聯通」兩個字,保存,關閉,再打開,發現變成了一個奇怪的字符,這是爲何呢?

首先windows默認的是ANSI編碼,而UTF-8編碼的標識頭規則以下圖

io9

因爲記事本是由編碼自己的規律判斷選取哪一個編碼表的

因此答案是,「聯通」這兩個字由ANSI編碼以後的碼流符合UTF-8的規則,則記事本自動識別是UTF-8的字符,而去查了UTF-8的碼錶

解決方法,咱們只要在聯通前面加上任意字符,記事本就不會誤判爲UTF-8解碼了

相關文章
相關標籤/搜索