Java IO最详解

 

一、流的概念
       流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。
       一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL。
        流的方向是重要的,根据流的方向,流可分为两类:输入流和输出流。用户可以从输入流中读取信息,但不能写它。相反,对输出流,只能往输入流写,而不能读它。
       实际上,流的源端和目的端可简单地看成是字节的生产者和消费者,对输入流,可不必关心它的源端是什么,只要简单地从流中读数据,而对输出流,也可不知道它的目的端,只是简单地往流中写数据。 
       形象的比喻——水流 ,文件======程序 ,文件和程序之间连接一个管道,水流就在之间形成了,自然也就出现了方向:可以流进,也可以流出.便于理解,这么定义流: 流就是一个管道里面有流水,这个管道连接了文件和程序。
二、流的分类
  1. java.io包中的类对应两类流,一类流直接从指定的位置(如磁盘文件或内存区域)读或写,这类流称为结点流(node stream),其它的流则称为过滤器(filters)。过滤器输入流往往是以其它输入流作为它的输入源,经过过滤或处理后再以新的输入流的形式提供给用户,过滤器输出流的原理也类似。 
  2. Java的常用输入、输出流
java.io包中的stream类根据它们操作对象的类型是字符还是字节可分为两大类: 字符流和字节流。
 
  • Java的字节流
InputStream是所有字节输入流的祖先,而OutputStream是所有字节输出流的祖先。
  • Java的字符流
Reader是所有读取字符串输入流的祖先,而writer是所有输出字符串的祖先。
结合开始所说的输入/输出流 ,出现了个一小框架。

                     字节流                         字符流
输入流        InputStream               Reader
输出流        OutputStream            Writer

JAVA字节流

  • FileInputStream和FileOutputStream
    这两个类属于结点流,第一个类的源端和第二个类的目的端都是磁盘文件,它们的构造方法允许通过文件的路径名来构造相应的流。如: 
    FileInputStream infile = new FileInputStream("myfile.dat");
    FileOutputStream outfile = new FileOutputStream("results.dat");

要注意的是,构造FileInputStream, 对应的文件必须存在并且是可读的,而构造FileOutputStream时,如输出文件已存在,则必须是可覆盖的。

  • BufferedInputStream和BufferedOutputStream
    它们是过滤器流,其作用是提高输入输出的效率。
  • DataInputStream和DataOutputStream
    这两个类创建的对象分别被称为数据输入流和数据输出流。这是很有用的两个流,它们允许程序按与机器无关的风格读写Java数据。所以比较适合于网络上的数据传输。这两个流也是过滤器流,常以其它流如InputStream或OutputStream作为它们的输入或输出。
Java的字符流

    字符流主要是用来处理字符的。Java采用16位的Unicode来表示字符串和字符,对应的字符流按输入和输出分别称为readers和writers。
  • InputStreamReader和OutputStreamWriter
    在构造这两个类对应的流时,它们会自动进行转换,将平台缺省的编码集编码的字节转换为Unicode字符。对英语环境,其缺省的编码集一般为ISO8859-1。
  • BufferedReader和BufferedWriter
    这两个类对应的流使用了缓冲,能大大提高输入输出的效率。这两个也是过滤器流,常用来对InputStreamReader和OutputStreamWriter进行处理。如:
复制代码
 1 import java.io.*;
 2 public class Echo {
 3   public static void main(String[] args) {
 4     BufferedReader in =
 5       new BufferedReader(
 6         new InputStreamReader(System.in));
 7     String s;
 8     try {
 9       while((s = in.readLine()).length() != 0)
10         System.out.println(s);
11       // An empty line terminates the program
12     } catch(IOException e) {
13       e.printStackTrace();
14     }
15   }
16 } 
复制代码
该程序接受键盘输入并回显。

对BufferedReader类,该类的readLine()方法能一次从流中读入一行,但对于BufferedWriter类,就没有一次写一行的方法,所以若要向流中一次写一行,可用PrintWriter类将原来的流改造成新的打印流,PrintWriter类有一个方法println(),能一次输出一行。如: 

............
PrintWriter out = new PrintWriter(new BufferedWriter(
      new FileWriter("D:\javacode\test.txt")));
out.println("Hello World!");
out.close();
............

 

 

例子:

1,与控制台相关。的读入/写出。 实现了字符串的复制。

 

复制代码
 1 import java.io.*;
 2 public class TextRead{
 3 
 4 public static void main(String[] args){
 5    BufferedReader bf = null;/*BufferedReader相当于一个大桶,其实就是内存,这里实现了大量大量的读写 ,而不是读一个字节或字符就直接写如硬盘,加强了对硬盘的保护。*/
 6    try{
 7     while(true){ // while(true){}循环保证程序不会结束
 8     
 9        bf = new BufferedReader(new InputStreamReader(System.in));
10        /*System.in 为标准输入,System.out为标准输出*/
11        /*InputStreamReader用语将字节流到字符流的转化,这也就是处理流了
12         *在这里相当与2个管道接在System.in与程序之间。
13         *readLine()方法功能比较好用,也就通过处理流来实现更好功能。
14         **/
15      String line = bf.readLine();
16      System.out.println(line);
17     }  
18    }catch(Exception e){
19     e.printStackTrace();
20    }finally{
21     //一定要关闭流,用完后。最好放在
22 
23 filally 里面。  
24     try{   
25      if(bf!=null){
26       bf.close();
27      }
28     }catch(Exception e){
29        e.printStackTrace();
30     }  
31    }  
32 }
33 }
复制代码

2,与文件 相关的 读写。    实现了文件的复制。

复制代码
 1 import java.io.*;
 2 public class TextRead{
 3 
 4 public static void main(String[] args){
 5    File fin,fout;  
 6    BufferedReader bf = null;
 7    PrintWriter pw = null;
 8    try{
 9     fin = new File("zzc.txt"); //注意文件与程序都要在同一个文件夹下。zzc.txt为要被复制的文件。
10     fout = new File("copyzzc.txt"); //如果没有会自动创建。
11     bf = new BufferedReader(new FileReader(fin));
12     pw = new PrintWriter(fout); //PrintWriter为打印流,也可以使用BufferedWriter.
13     String line = bf.readLine();
14     while(line!=null){
15     pw.println(line);
16      line = bf.readLine();
17     }
18    }catch(Exception e){
19     e.printStackTrace();
20    }finally{
21     try{
22     //关闭 文件。
23      if(bf!=null){
24       bf.close();
25       bf = null;
26      }
27      if(pw!=null){
28       pw.close();
29       pw = null;
30      }
31     }catch(Exception e){
32      e.printStackTrace();
33     }
34    }
35 }
36 }
复制代码

 

还有好多类:

像RandomAccessFile类,序列化接口,都十分重要。

Java有一种特殊类型的IO数据流——DataOutputStream——它可以保证“无论数据来自何种机器,只要使用一个DataInputStream收取这些数据,就可用本机正确的格式保存它们.

以后在把示例加上,还有写比较好的方法。

其实感觉这已经是固定模式了,一提到从键盘读取数据 就会联想到:

new BufferedReader(new InputStreamReader(System.in))

现在水平达不到,还是记些固定格式比较好,以至于会用。

某个老师也说过,创新是建立在扎实的基础之上,越来越觉得基础重要了。

初学java,一直搞不懂java里面的io关系,在网上找了很多大多都是给个结构图草草描述也看的不是很懂。而且没有结合到java7 的最新技术,所以自己来整理一下,有错的话请指正,也希望大家提出宝贵意见。

首先看个图:(如果你也是初学者,我相信你看了真个人都不好了,想想java设计者真是煞费苦心啊!)

 

这是java io 比较基本的一些处理流,除此之外我们还会提到一些比较深入的基于io的处理类,比如console类,SteamTokenzier,Externalizable接口,Serializable接口等等一些高级用法极其原理。

 

一、java io的开始:文件

1. 我们主要讲的是流,流的本质也是对文件的处理,我们循序渐进一步一步从文件将到流去。

2. java 处理文件的类 File,java提供了十分详细的文件处理方法,举了其中几个例子,其余的可以去

 

Java代码   收藏代码
  1. package com.hxw.io;  
  2. import java.io.*;  
  3.    
  4. public class FileExample{  
  5.     public static void main(String[] args) {  
  6.        
  7.         createFile();  
  8.     }  
  9.    
  10.   /** 
  11.    * 文件处理示例 
  12.    */  
  13.   public static void createFile() {  
  14.      File f=new File("E:/电脑桌面/jar/files/create.txt");  
  15.         try{  
  16.             f.createNewFile();  //当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。  
  17.             System.out.println("该分区大小"+f.getTotalSpace()/(1024*1024*1024)+"G"); //返回由此抽象路径名表示的文件或目录的名称。  
  18.             f.mkdirs();  //创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。  
  19. //            f.delete(); //  删除此抽象路径名表示的文件或目录  
  20.            System.out.println("文件名  "+f.getName());  //  返回由此抽象路径名表示的文件或目录的名称。  
  21.            System.out.println("文件父目录字符串 "+f.getParent());// 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。  
  22.             
  23.         }catch (Exception e) {  
  24.             e.printStackTrace();  
  25.         }  
  26.   }  
  27. }  



二、字节流:

1.字节流有输入和输出流,我们首先看输入流InputStream,我们首先解析一个例子(FileInputStream)。

 

Java代码   收藏代码
  1. package com.hxw.io;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. public class FileCount {  
  7.    /** 
  8.     * 我们写一个检测文件长度的小程序,别看这个程序挺长的,你忽略try catch块后发现也就那么几行而已。 
  9.     */  
  10.    publicstatic void main(String[] args) {  
  11.       //TODO 自动生成的方法存根  
  12.              int count=0;  //统计文件字节长度  
  13.       InputStreamstreamReader = null;   //文件输入流  
  14.       try{  
  15.           streamReader=newFileInputStream(new File("D:/David/Java/java 高级进阶/files/tiger.jpg"));  
  16.           /*1.new File()里面的文件地址也可以写成D:\\David\\Java\\java 高级进阶\\files\\tiger.jpg,前一个\是用来对后一个 
  17.            * 进行转换的,FileInputStream是有缓冲区的,所以用完之后必须关闭,否则可能导致内存占满,数据丢失。 
  18.           */  
  19.           while(streamReader.read()!=-1) {  //读取文件字节,并递增指针到下一个字节  
  20.              count++;  
  21.           }  
  22.           System.out.println("---长度是: "+count+" 字节");  
  23.       }catch (final IOException e) {  
  24.           //TODO 自动生成的 catch 块  
  25.           e.printStackTrace();  
  26.       }finally{  
  27.           try{  
  28.              streamReader.close();  
  29.           }catch (IOException e) {  
  30.              //TODO 自动生成的 catch 块  
  31.              e.printStackTrace();  
  32.           }  
  33.       }  
  34.    }  
  35.    
  36. }  



我们一步一步来,首先,上面的程序存在问题是,每读取一个自己我都要去用到FileInputStream,我输出的结果是“---长度是: 64982 字节”,那么进行了64982次操作!可能想象如果文件十分庞大,这样的操作肯定会出大问题,所以引出了缓冲区的概念。可以将streamReader.read()改成streamReader.read(byte[]b)此方法读取的字节数目等于字节数组的长度,读取的数据被存储在字节数组中,返回读取的字节数,InputStream还有其他方法mark,reset,markSupported方法,例如:

 

markSupported 判断该输入流能支持mark 和 reset 方法。

mark用于标记当前位置;在读取一定数量的数据(小于readlimit的数据)后使用reset可以回到mark标记的位置。

FileInputStream不支持mark/reset操作;BufferedInputStream支持此操作;

mark(readlimit)的含义是在当前位置作一个标记,制定可以重新读取的最大字节数,也就是说你如果标记后读取的字节数大于readlimit,你就再也回不到回来的位置了。

通常InputStream的read()返回-1后,说明到达文件尾,不能再读取。除非使用了mark/reset。

 

 

2.FileOutputStream 循序渐进版, InputStream是所有字节输出流的父类,子类有ByteArrayOutputStream,FileOutputStream,ObjectOutputStreanm,这些我们在后面都会一一说到。先说FileOutputStream

我以一个文件复制程序来说,顺便演示一下缓存区的使用。(Java I/O默认是不缓冲流的,所谓“缓冲”就是先把从流中得到的一块字节序列暂存在一个被称为buffer的内部字节数组里,然后你可以一下子取到这一整块的字节数据,没有缓冲的流只能一个字节一个字节读,效率孰高孰低一目了然。有两个特殊的输入流实现了缓冲功能,一个是我们常用的BufferedInputStream.)

 

Java代码   收藏代码
  1. package com.hxw.io;  
  2. import java.io.*;  
  3.    
  4. public class FileCopy {  
  5.    
  6.   public static void main(String[] args) {  
  7.      // TODO自动生成的方法存根  
  8.      byte[] buffer=new byte[512];   //一次取出的字节数大小,缓冲区大小  
  9.      int numberRead=0;  
  10.      FileInputStream input=null;  
  11.      FileOutputStream out =null;  
  12.      try {  
  13.         input=new FileInputStream("D:/David/Java/java 高级进阶/files/tiger.jpg");  
  14.         out=new FileOutputStream("D:/David/Java/java 高级进阶/files/tiger2.jpg"); //如果文件不存在会自动创建  
  15.          
  16.         while ((numberRead=input.read(buffer))!=-1) {  //numberRead的目的在于防止最后一次读取的字节小于buffer长度,  
  17.            out.write(buffer, 0, numberRead);       //否则会自动被填充0  
  18.         }  
  19.      } catch (final IOException e) {  
  20.         // TODO自动生成的 catch 块  
  21.         e.printStackTrace();  
  22.      }finally{  
  23.         try {  
  24.            input.close();  
  25.            out.close();  
  26.         } catch (IOException e) {  
  27.            // TODO自动生成的 catch 块  
  28.            e.printStackTrace();  
  29.         }  
  30.          
  31.      }  
  32.   }  
  33.    
  34. }  

 

 

3.读写对象:ObjectInputStream 和ObjectOutputStream ,该流允许读取或写入用户自定义的类,但是要实现这种功能,被读取和写入的类必须实现Serializable接口,其实该接口并没有什么方法,可能相当于一个标记而已,但是确实不合缺少的。实例代码如下:

 

Java代码   收藏代码
  1. package com.hxw.io;  
  2.    
  3. import java.io.*;  
  4.    
  5. public class ObjetStream {  
  6.    
  7.    /** 
  8.     * @param args 
  9.     */  
  10.    public static void main(String[] args) {  
  11.       // TODO自动生成的方法存根  
  12.       ObjectOutputStream objectwriter=null;  
  13.       ObjectInputStream objectreader=null;  
  14.        
  15.       try {  
  16.          objectwriter=new ObjectOutputStream(new FileOutputStream("D:/David/Java/java 高级进阶/files/student.txt"));  
  17.          objectwriter.writeObject(new Student("gg"22));  
  18.          objectwriter.writeObject(new Student("tt"18));  
  19.          objectwriter.writeObject(new Student("rr"17));  
  20.          objectreader=new ObjectInputStream(new FileInputStream("D:/David/Java/java 高级进阶/files/student.txt"));  
  21.          for (int i = 0; i < 3; i++) {  
  22.             System.out.println(objectreader.readObject());  
  23.          }  
  24.       } catch (IOException | ClassNotFoundException e) {  
  25.          // TODO自动生成的 catch 块  
  26.          e.printStackTrace();  
  27.       }finally{  
  28.          try {  
  29.             objectreader.close();  
  30.             objectwriter.close();  
  31.          } catch (IOException e) {  
  32.             // TODO自动生成的 catch 块  
  33.             e.printStackTrace();  
  34.          }  
  35.           
  36.       }  
  37.        
  38.    }  
  39.    
  40. }  
  41. class Student implements Serializable{  
  42.    private String name;  
  43.    private int age;  
  44.     
  45.    public Student(String name, int age) {  
  46.       super();  
  47.       this.name = name;  
  48.       this.age = age;  
  49.    }  
  50.    
  51.    @Override  
  52.    public String toString() {  
  53.       return "Student [name=" + name + ", age=" + age + "]";  
  54.    }  
  55.     
  56.     
  57. }  



运行后系统输出:

Student [name=gg, age=22]

Student [name=tt, age=18]

Student [name=rr, age=17]

4.有时没有必要存储整个对象的信息,而只是要存储一个对象的成员数据,成员数据的类型假设都是Java的基本数据类型,这样的需求不必使用到与Object输入、输出相关的流对象,可以使用DataInputStream、DataOutputStream来写入或读出数据。下面是一个例子:(DataInputStream的好处在于在从文件读出数据时,不用费心地自行判断读入字符串时或读入int类型时何时将停止,使用对应的readUTF()和readInt()方法就可以正确地读入完整的类型数据。)

 

 

Java代码   收藏代码
  1. package com.hxw;  
  2. public class Member {  
  3.     private String name;  
  4.     private int age;  
  5.     public Member() {  
  6.     }  
  7.    public Member(String name, int age) {  
  8.         this.name = name;  
  9.         this.age = age;  
  10.     }  
  11.     public void setName(String name){  
  12.         this.name = name;  
  13.     }  
  14.     public void setAge(int age) {  
  15.         this.age = age;  
  16.     }  
  17.     public String getName() {  
  18.         return name;  
  19.     }  
  20.     public int getAge() {  
  21.         return age;  
  22.     }  
  23. }  



打算将Member类实例的成员数据写入文件中,并打算在读入文件数据后,将这些数据还原为Member对象。下面的代码简单示范了如何实现这个需求。

 

 

Java代码   收藏代码
  1. package com.hxw;  
  2. import java.io.*;  
  3. public class DataStreamDemo  
  4. {  
  5.   public static void main(String[]args)  
  6.   {  
  7.      Member[] members = {newMember("Justin",90),  
  8.                         newMember("momor",95),  
  9.                         newMember("Bush",88)};  
  10.         try  
  11.      {  
  12.         DataOutputStreamdataOutputStream = new DataOutputStream(new FileOutputStream(args[0]));  
  13.    
  14.         for(Member member:members)  
  15.         {  
  16.             //写入UTF字符串  
  17.            dataOutputStream.writeUTF(member.getName());  
  18.            //写入int数据  
  19.            dataOutputStream.writeInt(member.getAge());  
  20.         }  
  21.    
  22.         //所有数据至目的地  
  23.         dataOutputStream.flush();  
  24.         //关闭流  
  25.         dataOutputStream.close();  
  26.    
  27.             DataInputStreamdataInputStream = new DataInputStream(new FileInputStream(args[0]));  
  28.    
  29.         //读出数据并还原为对象  
  30.         for(inti=0;i<members.length;i++)  
  31.         {  
  32.            //读出UTF字符串  
  33.            String name =dataInputStream.readUTF();  
  34.            //读出int数据  
  35.            int score =dataInputStream.readInt();  
  36.            members[i] = newMember(name,score);  
  37.         }  
  38.    
  39.         //关闭流  
  40.         dataInputStream.close();  
  41.    
  42.         //显示还原后的数据  
  43.         for(Member member : members)  
  44.         {  
  45.            System.out.printf("%s\t%d%n",member.getName(),member.getAge());  
  46.         }  
  47.      }  
  48.      catch(IOException e)  
  49.      {  
  50.             e.printStackTrace();  
  51.      }  
  52.   }  
  53. }  



5.PushbackInputStream类继承了FilterInputStream类是iputStream类的修饰者。提供可以将数据插入到输入流前端的能力(当然也可以做其他操作)。简而言之PushbackInputStream类的作用就是能够在读取缓冲区的时候提前知道下一个字节是什么,其实质是读取到下一个字符后回退的做法,这之间可以进行很多操作,这有点向你把读取缓冲区的过程当成一个数组的遍历,遍历到某个字符的时候可以进行的操作,当然,如果要插入,能够插入的最大字节数是与推回缓冲区的大小相关的,插入字符肯定不能大于缓冲区吧!下面是一个示例。

 

Java代码   收藏代码
  1. package com.hxw.io;  
  2.    
  3. import java.io.ByteArrayInputStream; //导入ByteArrayInputStream的包  
  4. import java.io.IOException;  
  5. import java.io.PushbackInputStream;  
  6.    
  7. /** 
  8.  * 回退流操作 
  9.  * */  
  10. public class PushBackInputStreamDemo {  
  11. public static void main(String[] args) throws IOException {  
  12.     String str = "hello,rollenholt";  
  13.     PushbackInputStream push = null// 声明回退流对象  
  14.     ByteArrayInputStream bat = null// 声明字节数组流对象  
  15.     bat = new ByteArrayInputStream(str.getBytes());  
  16.     push = new PushbackInputStream(bat); // 创建回退流对象,将拆解的字节数组流传入  
  17.     int temp = 0;  
  18.     while ((temp = push.read()) != -1) { // push.read()逐字节读取存放在temp中,如果读取完成返回-1  
  19.        if (temp == ',') { // 判断读取的是否是逗号  
  20.           push.unread(temp); //回到temp的位置  
  21.           temp = push.read(); //接着读取字节  
  22.           System.out.print("(回退" + (char) temp + ") "); // 输出回退的字符  
  23.        } else {  
  24.           System.out.print((char) temp); // 否则输出字符  
  25.        }  
  26.     }  
  27. }  
  28. }  



6.SequenceInputStream:有些情况下,当我们需要从多个输入流中向程序读入数据。此时,可以使用合并流,将多个输入流合并成一个SequenceInputStream流对象。SequenceInputStream会将与之相连接的流集组合成一个输入流并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 合并流的作用是将多个源合并合一个源。其可接收枚举类所封闭的多个字节流对象。

 

Java代码   收藏代码
  1. package com.hxw.io;  
  2.    
  3. import java.io.*;  
  4. import java.util.Enumeration;  
  5. import java.util.Vector;  
  6.    
  7. public class SequenceInputStreamTest {  
  8.   /** 
  9.    * @param args 
  10.    *            SequenceInputStream合并流,将与之相连接的流集组合成一个输入流并从第一个输入流开始读取, 
  11.    *            直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 
  12.    *            合并流的作用是将多个源合并合一个源。可接收枚举类所封闭的多个字节流对象。 
  13.    */  
  14.   public static void main(String[] args) {  
  15.      doSequence();  
  16.   }  
  17.    
  18.   private static void doSequence() {  
  19.      // 创建一个合并流的对象  
  20.      SequenceInputStream sis = null;  
  21.      // 创建输出流。  
  22.      BufferedOutputStream bos = null;  
  23.      try {  
  24.         // 构建流集合。  
  25.         Vector<InputStream> vector = new Vector<InputStream>();  
  26.         vector.addElement(new FileInputStream("D:\text1.txt"));  
  27.         vector.addElement(new FileInputStream("D:\text2.txt"));  
  28.         vector.addElement(new FileInputStream("D:\text3.txt"));  
  29.         Enumeration<InputStream> e = vector.elements();  
  30.    
  31.         sis = new SequenceInputStream(e);  
  32.    
  33.         bos = new BufferedOutputStream(new FileOutputStream("D:\text4.txt"));  
  34.         // 读写数据  
  35.         byte[] buf = new byte[1024];  
  36.         int len = 0;  
  37.         while ((len = sis.read(buf)) != -1) {  
  38.            bos.write(buf, 0, len);  
  39.            bos.flush();  
  40.         }  
  41.      } catch (FileNotFoundException e1) {  
  42.         e1.printStackTrace();  
  43.      } catch (IOException e1) {  
  44.         e1.printStackTrace();  
  45.      } finally {  
  46.         try {  
  47.            if (sis != null)  
  48.               sis.close();  
  49.         } catch (IOException e) {  
  50.            e.printStackTrace();  
  51.         }  
  52.         try {  
  53.            if (bos != null)  
  54.               bos.close();  
  55.         } catch (IOException e) {  
  56.            e.printStackTrace();  
  57.         }  
  58.      }  
  59.   }  
  60. }  



7.PrintStream 说这个名字可能初学者不熟悉,如果说System.out.print()你肯定熟悉,System.out这个对象就是PrintStream,这个我们不做过多示例

三、字符流(顾名思义,就是操作字符文件的流)

1.java 使用Unicode存储字符串,在写入字符流时我们都可以指定写入的字符串的编码。前面介绍了不用抛异常的处理字节型数据的流ByteArrayOutputStream,与之对应的操作字符类的类就是CharArrayReader,CharArrayWriter类,这里也会用到缓冲区,不过是字符缓冲区,一般讲字符串放入到操作字符的io流一般方法是

CharArrayReaderreader=mew CharArrayReader(str.toCharArray()); 一旦会去到CharArrayReader实例就可以使用CharArrayReader访问字符串的各个元素以执行进一步读取操作。不做例子

2.我们用FileReader ,PrintWriter来做示范

 

Java代码   收藏代码
  1. package com.hxw.io;  
  2.    
  3. import java.io.FileNotFoundException;  
  4. import java.io.FileReader;  
  5. import java.io.IOException;  
  6. import java.io.PrintWriter;  
  7. import java.nio.CharBuffer;  
  8.    
  9. public class Print {  
  10.    
  11. /** 
  12.  * @param args 
  13.  */  
  14. public static void main(String[] args) {  
  15.     // TODO自动生成的方法存根  
  16.     char[] buffer=new char[512];   //一次取出的字节数大小,缓冲区大小  
  17.     int numberRead=0;  
  18.     FileReader reader=null;        //读取字符文件的流  
  19.     PrintWriter writer=null;    //写字符到控制台的流  
  20.      
  21.     try {  
  22.        reader=new FileReader("D:/David/Java/java 高级进阶/files/copy1.txt");  
  23.        writer=new PrintWriter(System.out);  //PrintWriter可以输出字符到文件,也可以输出到控制台  
  24.        while ((numberRead=reader.read(buffer))!=-1) {  
  25.           writer.write(buffer, 0, numberRead);  
  26.        }  
  27.     } catch (IOException e) {  
  28.        // TODO自动生成的 catch 块  
  29.        e.printStackTrace();  
  30.     }finally{  
  31.        try {  
  32.           reader.close();  
  33.        } catch (IOException e) {  
  34.           // TODO自动生成的 catch 块  
  35.           e.printStackTrace();  
  36.        }  
  37.        writer.close();       //这个不用抛异常  
  38.     }  
  39.         
  40. }  
  41.    
  42. }  



3.相对我们前面的例子是直接用FileReader打开的文件,我们这次使用链接流,一般比较常用的都用链接流,所谓链接流就是就多次对流的封装,这样能更好的操作个管理数据,(比如我们利用DataInputStream(BufferedInputStream(FileInputStream))将字节流层层包装后,我们可以读取readByte(),readChar()这样更加具体的操作,注意,该流属于字节流对字符进行操作,)字符流用CharArrayReader就可以了。下面的示例我们将用到j2se 5中的一个可变参数进行一个小度扩展。使用BufferedWriter 和BufferedReader用文件级联的方式进行写入,即将多个文件写入到同一文件中(自带缓冲区的输出输出流BufferedReader和BufferedWriter,该流最常用的属readLine()方法了,读取一行数据,并返回String)。

 

 

Java代码   收藏代码
  1. package com.hxw.io;  
  2.    
  3. import java.io.BufferedReader;  
  4. import java.io.BufferedWriter;  
  5. import java.io.FileReader;  
  6. import java.io.FileWriter;  
  7. import java.io.IOException;  
  8. import java.util.Iterator;  
  9.    
  10. public class FileConcatenate {  
  11.    
  12.   /** 
  13.    * 包装类进行文件级联操作 
  14.    */  
  15.   public static void main(String[] args) {  
  16.      // TODO自动生成的方法存根  
  17.      try {  
  18.         concennateFile(args);  
  19.      } catch (IOException e) {  
  20.         // TODO自动生成的 catch 块  
  21.         e.printStackTrace();  
  22.      }  
  23.   }  
  24.   public static voidconcennateFile(String...fileName) throws IOException{  
  25.      String str;  
  26.      //构建对该文件您的输入流  
  27.      BufferedWriter writer=new BufferedWriter(new FileWriter("D:/David/Java/java 高级进阶/files/copy2.txt"));  
  28.      for(String name: fileName){  
  29.         BufferedReader reader=new BufferedReader(new FileReader(name));  
  30.          
  31.         while ((str=reader.readLine())!=null) {  
  32.            writer.write(str);  
  33.            writer.newLine();  
  34.         }  
  35.      }  
  36.   }  
  37.    
  38. }  
  39.    



4.Console类,该类提供了用于读取密码的方法,可以禁止控制台回显并返回char数组,对两个特性对保证安全有作用,平时用的不多,了解就行。

5.StreamTokenizer 类,这个类非常有用,它可以把输入流解析为标记(token), StreamTokenizer 并非派生自InputStream或者OutputStream,而是归类于io库中,因为StreamTokenizer只处理InputStream对象。

首先给出我的文本文件内容:

'水上漂'

青青草

"i love wyhss"

{3211}

23223 3523

i love wyh ,。

. ,

下面是代码:

 

Java代码   收藏代码
  1. package com.hxw.io;  
  2. import java.io.BufferedReader;  
  3. import java.io.FileReader;  
  4. import java.io.IOException;  
  5. import java.io.StreamTokenizer;  
  6.    
  7. /** 
  8.  * 使用StreamTokenizer来统计文件中的字符数 
  9.  * StreamTokenizer 类获取输入流并将其分析为“标记”,允许一次读取一个标记。 
  10.  * 分析过程由一个表和许多可以设置为各种状态的标志控制。 
  11.  * 该流的标记生成器可以识别标识符、数字、引用的字符串和各种注释样式。 
  12.  * 
  13.  *  默认情况下,StreamTokenizer认为下列内容是Token: 字母、数字、除C和C++注释符号以外的其他符号。 
  14.  *  如符号"/"不是Token,注释后的内容也不是,而"\"是Token。单引号和双引号以及其中的内容,只能算是一个Token。 
  15.  *  统计文章字符数的程序,不是简单的统计Token数就万事大吉,因为字符数不等于Token。按照Token的规定, 
  16.  *  引号中的内容就算是10页也算一个Token。如果希望引号和引号中的内容都算作Token,应该调用下面的代码: 
  17.  *    st.ordinaryChar('\''); 
  18.  * st.ordinaryChar('\"'); 
  19.  */  
  20. public class StreamTokenizerExample {  
  21.    
  22.     /** 
  23.      * 统计字符数 
  24.      * @param fileName 文件名 
  25.      * @return    字符数 
  26.      */  
  27. public static void main(String[] args) {  
  28.         String fileName = "D:/David/Java/java 高级进阶/files/copy1.txt";  
  29.         StreamTokenizerExample.statis(fileName);  
  30.     }  
  31.     public static long statis(String fileName) {  
  32.    
  33.         FileReader fileReader = null;  
  34.         try {  
  35.             fileReader = new FileReader(fileName);  
  36.             //创建分析给定字符流的标记生成器  
  37.             StreamTokenizer st = new StreamTokenizer(new BufferedReader(  
  38.                     fileReader));  
  39.    
  40.             //ordinaryChar方法指定字符参数在此标记生成器中是“普通”字符。  
  41.             //下面指定单引号、双引号和注释符号是普通字符  
  42.             st.ordinaryChar('\'');  
  43.             st.ordinaryChar('\"');  
  44.             st.ordinaryChar('/');  
  45.    
  46.             String s;  
  47.             int numberSum = 0;  
  48.             int wordSum = 0;  
  49.             int symbolSum = 0;  
  50.             int total = 0;  
  51.             //nextToken方法读取下一个Token.  
  52.             //TT_EOF指示已读到流末尾的常量。  
  53.             while (st.nextToken() !=StreamTokenizer.TT_EOF) {  
  54.                 //在调用 nextToken 方法之后,ttype字段将包含刚读取的标记的类型  
  55.                 switch (st.ttype) {  
  56.                 //TT_EOL指示已读到行末尾的常量。  
  57.                 case StreamTokenizer.TT_EOL:  
  58.                     break;  
  59.                 //TT_NUMBER指示已读到一个数字标记的常量  
  60.                 case StreamTokenizer.TT_NUMBER:  
  61.                     //如果当前标记是一个数字,nval字段将包含该数字的值  
  62.                     s = String.valueOf((st.nval));  
  63.                     System.out.println("数字有:"+s);  
  64.                     numberSum ++;  
  65.                     break;  
  66.                 //TT_WORD指示已读到一个文字标记的常量  
  67.                 case StreamTokenizer.TT_WORD:  
  68.                     //如果当前标记是一个文字标记,sval字段包含一个给出该文字标记的字符的字符串  
  69.                     s = st.sval;  
  70.                     System.out.println("单词有: "+s);  
  71.                     wordSum ++;  
  72.                     break;  
  73.                 default:  
  74.                     //如果以上3中类型都不是,则为英文的标点符号  
  75.                     s = String.valueOf((char) st.ttype);  
  76.                     System.out.println("标点有: "+s);  
  77.                     symbolSum ++;  
  78.                 }  
  79.             }  
  80.             System.out.println("数字有 " + numberSum+"个");  
  81.             System.out.println("单词有 " + wordSum+"个");  
  82.             System.out.println("标点符号有: " + symbolSum+"个");  
  83.             total = symbolSum + numberSum +wordSum;  
  84.             System.out.println("Total = " + total);  
  85.             return total;  
  86.         } catch (Exception e) {  
  87.             e.printStackTrace();  
  88.             return -1;  
  89.         } finally {  
  90.             if (fileReader != null) {  
  91.                 try {  
  92.                     fileReader.close();  
  93.                 } catch (IOException e1) {  
  94.                 }  
  95.             }  
  96.         }  
  97.     }  
  98.    
  99.      
  100. }  



运行结果为:

标点有: '

单词有: 水上漂

标点有: '

单词有: 青青草

标点有: "

单词有: i

单词有: love

单词有: wyh

单词有: ss

标点有: "

标点有: {

数字有:3211.0

标点有: }

数字有:23223.0

数字有:35.23

单词有: i

单词有: love

单词有: wyh

单词有: ,。

数字有:0.0

标点有: ,

数字有 4个

单词有 10个

标点符号有: 7个

Total= 21

 

我们从其中可以看到很多东西:

1.一个单独的小数点“.”是被当做一个数字来对待的,数字的值为0.0;

2.一串汉字只要中间没有符号(空格回车 分号等等)都是被当做一个单词的。中文的标点跟中文的汉字一样处理

3.如果不对引号化成普通字符,一个引号内的内容不论多少都被当做是一个标记。

4.该类能够识别英文标点

 

6. java io里面还有其他接口类似Serializable接口的子接口Externalizable接口,比Serializable复杂一些,这里不再介绍。还有关于java对象版本化的东西感兴趣的可以百度。java nio的东西这里没有涉及,后续会结合到线程再发一篇文章专门解析这个东西。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值