• Java笔记(二十八)……IO流下 IO包中其他常用类以及编码表问题


    PrintWriter打印流

    Writer的子类,既可以接收字符流,也可以接收字节流,还可以接收文件名或者文件对象,非常方便

    同时,还可以设置自动刷新以及保持原有格式写入各种文本类型的print方法

    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

    将对象存取在硬盘上,叫做对象的持久化存储(存储的是对象的属性值,而不是方法)

    想要对对象进行序列化,该对象必须实现Serializable接口,Serializable接口没有方法,称为标记接口,实现过程只是给实现者加入一个序列化的ID:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 其实就是序列号,这个序列号是由变量的声明语句自动生成的,我们也可以自己定义类的序列号

    对象序列化的小例子

       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修饰的变量是不会被序列化到硬盘上的

    管道流

    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: }

    随机访问文件流

    RandomAccessFile

    直接继承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解码了

  • 相关阅读:
    广域网详解
    无线AP和无线路由器区别
    TRUNK的作用功能.什么是TRUNK
    name after, name for, name as
    让你的情商爆棚吧!
    综合布线系统之7个子系统构成
    网桥和交换机的工作原理及区别
    边界网关协议BGP
    OSPF协议详解
    路由信息协议(RIP)的防环机制
  • 原文地址:https://www.cnblogs.com/ShawnWithSmallEyes/p/3390740.html
Copyright © 2020-2023  润新知