Java IO

来源:互联网 发布:mac os x 10.7.5软件 编辑:程序博客网 时间:2024/06/04 01:31

转载自http://davidisok.iteye.com/blog/2106489

Java IO 详解

初学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的东西这里没有涉及,后续会结合到线程再发一篇文章专门解析这个东西。





转载自http://blog.csdn.net/akon_vm/article/details/7429245

RandomAccessFile

RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。但是该类仅限于操作文件。

RandomAccessFile不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至不使用InputStream和OutputStream类中已经存在的任何功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。

基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream结合起来,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )、skipBytes()跳过多少字节数。此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件。

只有RandomAccessFile才有seek搜寻方法,而这个方法也只适用于文件。BufferedInputStream有一个mark( )方法,你可以用它来设定标记(把结果保存在一个内部变量里),然后再调用reset( )返回这个位置,但是它的功能太弱了,而且也不怎么实用。

RandomAccessFile的绝大多数功能,但不是全部,已经被JDK 1.4的nio的"内存映射文件(memory-mapped files)"给取代了,你该考虑一下是不是用"内存映射文件"来代替RandomAccessFile了。

[java] view plain copy
  1. import java.io.IOException;  
  2. import java.io.RandomAccessFile;  
  3.   
  4. public class TestRandomAccessFile {  
  5.     public static void main(String[] args) throws IOException {  
  6.         RandomAccessFile rf = new RandomAccessFile("rtest.dat""rw");  
  7.         for (int i = 0; i < 10; i++) {  
  8.             //写入基本类型double数据  
  9.             rf.writeDouble(i * 1.414);  
  10.         }  
  11.         rf.close();  
  12.         rf = new RandomAccessFile("rtest.dat""rw");  
  13.         //直接将文件指针移到第5个double数据后面  
  14.         rf.seek(5 * 8);  
  15.         //覆盖第6个double数据  
  16.         rf.writeDouble(47.0001);  
  17.         rf.close();  
  18.         rf = new RandomAccessFile("rtest.dat""r");  
  19.         for (int i = 0; i < 10; i++) {  
  20.             System.out.println("Value " + i + ": " + rf.readDouble());  
  21.         }  
  22.         rf.close();  
  23.     }  
  24. }   


 

内存映射文件

内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件。有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问。这种解决办法能大大简化修改文件的代码。
fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中。注意,你必须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大;也就是说,它还可以映射一个大文件的某个小片断。


MappedByteBuffer是ByteBuffer的子类,因此它具备了ByteBuffer的所有方法,但新添了force()将缓冲区的内容强制刷新到存储设备中去、load()将存储设备中的数据加载到内存中、isLoaded()位置内存中的数据是否与存储设置上同步。这里只简单地演示了一下put()和get()方法,除此之外,你还可以使用asCharBuffer( )之类的方法得到相应基本类型数据的缓冲视图后,可以方便的读写基本类型数据。

[java] view plain copy
  1. import java.io.RandomAccessFile;  
  2. import java.nio.MappedByteBuffer;  
  3. import java.nio.channels.FileChannel;  
  4.   
  5. public class LargeMappedFiles {  
  6.     static int length = 0x8000000// 128 Mb  
  7.   
  8.     public static void main(String[] args) throws Exception {  
  9.         // 为了以可读可写的方式打开文件,这里使用RandomAccessFile来创建文件。  
  10.         FileChannel fc = new RandomAccessFile("test.dat""rw").getChannel();  
  11.         //注意,文件通道的可读可写要建立在文件流本身可读写的基础之上  
  12.         MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length);  
  13.         //写128M的内容  
  14.         for (int i = 0; i < length; i++) {  
  15.             out.put((byte'x');  
  16.         }  
  17.         System.out.println("Finished writing");  
  18.         //读取文件中间6个字节内容  
  19.         for (int i = length / 2; i < length / 2 + 6; i++) {  
  20.             System.out.print((char) out.get(i));  
  21.         }  
  22.         fc.close();  
  23.     }  
  24. }  


 

尽管映射写似乎要用到FileOutputStream,但是映射文件中的所有输出 必须使用RandomAccessFile,但如果只需要读时可以使用FileInputStream,写映射文件时一定要使用随机访问文件,可能写时要读的原因吧。

 

该程序创建了一个128Mb的文件,如果一次性读到内存可能导致内存溢出,但这里访问好像只是一瞬间的事,这是因为,真正调入内存的只是其中的一小部分,其余部分则被放在交换文件上。这样你就可以很方便地修改超大型的文件了(最大可以到2 GB)。注意,Java是调用操作系统的"文件映射机制"来提升性能的。

 

RandomAccessFile类的应用:

[java] view plain copy
  1. /* 
  2.  * 程序功能:演示了RandomAccessFile类的操作,同时实现了一个文件复制操作。 
  3.  */  
  4. package com.lwj.demo;  
  5.   
  6. import java.io.*;  
  7.   
  8. public class RandomAccessFileDemo {  
  9.  public static void main(String[] args) throws Exception {  
  10.   RandomAccessFile file = new RandomAccessFile("file""rw");  
  11.   // 以下向file文件中写数据  
  12.   file.writeInt(20);// 占4个字节  
  13.   file.writeDouble(8.236598);// 占8个字节  
  14.   file.writeUTF("这是一个UTF字符串");// 这个长度写在当前文件指针的前两个字节处,可用readShort()读取  
  15.   file.writeBoolean(true);// 占1个字节  
  16.   file.writeShort(395);// 占2个字节  
  17.   file.writeLong(2325451l);// 占8个字节  
  18.   file.writeUTF("又是一个UTF字符串");  
  19.   file.writeFloat(35.5f);// 占4个字节  
  20.   file.writeChar('a');// 占2个字节  
  21.   
  22.   file.seek(0);// 把文件指针位置设置到文件起始处  
  23.   
  24.   // 以下从file文件中读数据,要注意文件指针的位置  
  25.   System.out.println("——————从file文件指定位置读数据——————");  
  26.   System.out.println(file.readInt());  
  27.   System.out.println(file.readDouble());  
  28.   System.out.println(file.readUTF());  
  29.   
  30.   file.skipBytes(3);// 将文件指针跳过3个字节,本例中即跳过了一个boolean值和short值。  
  31.   System.out.println(file.readLong());  
  32.   
  33.   file.skipBytes(file.readShort()); // 跳过文件中“又是一个UTF字符串”所占字节,注意readShort()方法会移动文件指针,所以不用加2。  
  34.   System.out.println(file.readFloat());  
  35.     
  36.   //以下演示文件复制操作  
  37.   System.out.println("——————文件复制(从file到fileCopy)——————");  
  38.   file.seek(0);  
  39.   RandomAccessFile fileCopy=new RandomAccessFile("fileCopy","rw");  
  40.   int len=(int)file.length();//取得文件长度(字节数)  
  41.   byte[] b=new byte[len];  
  42.   file.readFully(b);  
  43.   fileCopy.write(b);  
  44.   System.out.println("复制完成!");  
  45.  }  
  46. }  


RandomAccessFile 插入写示例:

[java] view plain copy
  1. /** 
  2.  *  
  3.  * @param skip 跳过多少过字节进行插入数据 
  4.  * @param str 要插入的字符串 
  5.  * @param fileName 文件路径 
  6.  */  
  7. public static void beiju(long skip, String str, String fileName){  
  8.     try {  
  9.         RandomAccessFile raf = new RandomAccessFile(fileName,"rw");  
  10.         if(skip <  0 || skip > raf.length()){  
  11.             System.out.println("跳过字节数无效");  
  12.             return;  
  13.         }  
  14.         byte[] b = str.getBytes();  
  15.         raf.setLength(raf.length() + b.length);  
  16.         for(long i = raf.length() - 1; i > b.length + skip - 1; i--){  
  17.             raf.seek(i - b.length);  
  18.             byte temp = raf.readByte();  
  19.             raf.seek(i);  
  20.             raf.writeByte(temp);  
  21.         }  
  22.         raf.seek(skip);  
  23.         raf.write(b);  
  24.         raf.close();  
  25.     } catch (Exception e) {  
  26.         e.printStackTrace();  
  27.     }  
  28. }  


 

利用RandomAccessFile实现文件的多线程下载,即多线程下载一个文件时,将文件分成几块,每块用不同的线程进行下载。下面是一个利用多线程在写文件时的例子,其中预先分配文件所需要的空间,然后在所分配的空间中进行分块,然后写入:

[java] view plain copy
  1. import java.io.FileNotFoundException;  
  2. import java.io.IOException;  
  3. import java.io.RandomAccessFile;  
  4.   
  5. /** 
  6.  * 测试利用多线程进行文件的写操作 
  7.  */  
  8. public class Test {  
  9.   
  10.     public static void main(String[] args) throws Exception {  
  11.         // 预分配文件所占的磁盘空间,磁盘中会创建一个指定大小的文件  
  12.         RandomAccessFile raf = new RandomAccessFile("D://abc.txt""rw");  
  13.         raf.setLength(1024*1024); // 预分配 1M 的文件空间  
  14.         raf.close();  
  15.           
  16.         // 所要写入的文件内容  
  17.         String s1 = "第一个字符串";  
  18.         String s2 = "第二个字符串";  
  19.         String s3 = "第三个字符串";  
  20.         String s4 = "第四个字符串";  
  21.         String s5 = "第五个字符串";  
  22.           
  23.         // 利用多线程同时写入一个文件  
  24.         new FileWriteThread(1024*1,s1.getBytes()).start(); // 从文件的1024字节之后开始写入数据  
  25.         new FileWriteThread(1024*2,s2.getBytes()).start(); // 从文件的2048字节之后开始写入数据  
  26.         new FileWriteThread(1024*3,s3.getBytes()).start(); // 从文件的3072字节之后开始写入数据  
  27.         new FileWriteThread(1024*4,s4.getBytes()).start(); // 从文件的4096字节之后开始写入数据  
  28.         new FileWriteThread(1024*5,s5.getBytes()).start(); // 从文件的5120字节之后开始写入数据  
  29.     }  
  30.       
  31.     // 利用线程在文件的指定位置写入指定数据  
  32.     static class FileWriteThread extends Thread{  
  33.         private int skip;  
  34.         private byte[] content;  
  35.           
  36.         public FileWriteThread(int skip,byte[] content){  
  37.             this.skip = skip;  
  38.             this.content = content;  
  39.         }  
  40.           
  41.         public void run(){  
  42.             RandomAccessFile raf = null;  
  43.             try {  
  44.                 raf = new RandomAccessFile("D://abc.txt""rw");  
  45.                 raf.seek(skip);  
  46.                 raf.write(content);  
  47.             } catch (FileNotFoundException e) {  
  48.                 e.printStackTrace();  
  49.             } catch (IOException e) {  
  50.                 // TODO Auto-generated catch block  
  51.                 e.printStackTrace();  
  52.             } finally {  
  53.                 try {  
  54.                     raf.close();  
  55.                 } catch (Exception e) {  
  56.                 }  
  57.             }  
  58.         }  
  59.     }  
  60.   
  61. }  


 

 

 

 




原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小米手机账户验证码隐藏了怎么办? 苹果手机绑定的邮箱密码忘了怎么办 魔域手机版不要的装备怎么办 魔域永恒多了的幻兽装备怎么办 魔域怀旧版70星以后宝宝怎么办 天猫确认收货后不寄发票怎么办 足球运动员踢了一半想撒尿了怎么办 17个月宝宝0型腿怎么办 走的路多了腿疼怎么办 如果新兵老被老兵打怎么办求求个位 做古董拍卖诈骗被警察抓了怎么办 武汉个人社保怎么办停转到海南来 狗狗车祸前脚软组织受伤了怎么办 6岁儿童夜里盗汗惊战发抖怎么办 一个月大的小狗尾巴掉毛怎么办 口袋妖怪用修改器被ban了怎么办 联币金融提现不到账怎么办 秘密花园的手抄报怎么办?五年级 雷蛇鼠标不亮了怎么办呢 汽车后备箱的垫子好臭怎么办 摩托尾箱内衬味道太重怎么办 买房子把办贷款的单据丢了怎么办 鞋盒破损卖家拒绝退货怎么办 顺丰快递退回发件人签收失败怎么办 客户收到衣服后说衣服破损怎么办 纱窗被老鼠咬了个洞怎么办 双色印刷机开槽刀调不动怎么办 水管软管两端的螺帽下包生锈怎么办 棉花被子盖时间长了被芯变小怎么办 垫的被子发霉了啊没有地方晒怎么办 小孩拉尿在丝棉被棉怎么办 把兔子养大了竟然会咬人怎么办 小兔子生下来母兔子不喂奶怎么办 11个月的宝宝肚子有小白虫怎么办 电焊把脸烤了痒的不行怎么办? 还没满月的宝宝吐奶怎么办 周岁宝宝发烧腹泻吃药老吐怎么办 生完宝宝后肚皮很黑怎么办 两个月宝宝抵抗力差总生病怎么办 两岁宝宝只会说几个简单的词怎么办 两岁宝宝对绘本不感兴趣怎么办