最近由于要分析log,但是由于log文件太大而不能用编辑器打开所以学要分割文件。
于是想了两三个方法。
1,shell命令分割。没找到。那位达人了解的给个提示啊。
2,软件分割。免费软件不能分割大文件。
3,程序分割。关于3,我写了一段程序。
想当然的我用RandomAccessFile读文件,然后用FileOutputStream输出。
发现效率十分低下。3g文件 500m一个,文成一个就将近3分钟。
于是改成了如下形式FileInputStream in = new FileInputStream(srcFile);
FileChannel channel = in.getChannel();
         
FileOutputStream out = new FileOutputStream(fileSeparateName);
FileChannel outChannel = out.getChannel();
          
channel.transferTo(beginPos, blockSize, outChannel);
这样一来速度有了明显提升。但是在分割小文件(几十M)时速度又不如第一个方法。于是产生了下面的疑惑。
1,在什么样的情况下channel要好于stream.有没有具体的临界点。
2,从这个情况来看 java 的io 在不同的情况下可能要使用不同的类组合来达到最好的效率。哪个达人可以指点一下。或者大家来讨论一下啊。

解决方案 »

  1.   

    不打用。不怎么了解。
    不過如果條件允許的話
    你可以大概判斷下小于100用stream不然用channel
      

  2.   

    RandomAccessFile方法貌似更多用于定点读写...
    Reader/Writer和FileChannel在文本读写上都不错,后者是jdk文档里推荐使用的,据说jdk1.6中io性能有很大提升喵`~~`
      

  3.   

    我觉得火龙果说的意见十分中肯,当你的日志被分为两分的时候,有些条目必然被破坏,实际会增加你处理这些数据的难度,
    也就是说,你整个处理条目的逻辑会复杂,速度会有一些影响。LineNumberReader lnr = new 你应该用这个,我觉得这是你应该用的类。而且文件读取,你把内容全部存放到内存里,是不可避免的,以前我分析日志的时候,恐怕比你做的日志要多得多。
    Java还是能勉强应付的,。
      

  4.   

    我这里一天的日志光sql部分就是3g,叶面处理,vm,console的日志会小一些。
    一天的日志都会打成gz文件。不过拿到本地打开的话就是3g了,这个没办法啊。
    我也想小一点啊。谢谢甘草 的建议 我会再去查一下LineNumberReader的。关于日志的分析,不知道大家都是如何做的。
    我现在是客户那边在出错之后告诉我时间和错误摘要。
    然后我从日志里分析。这样的话只要几个时间点的日志就好了。也就是分割之后
    我grep一下 找到目标打开文件直接看日志,分析错误。不知道火龙果前辈所说的分析是怎样的分析啊?
    我还很菜 希望大家多多指点
      

  5.   

    看了LineNumberReader的reference.又学习了。按照字节分文件的做法基于以下两点考虑,请大家看看,指正一下
    1,我需要分析的一般是一个时间点前后的日志。比如8点10分出错那么我只想看
    8点到8点10分的日志。这样的话是不是一行被截断并不是大问题。
    但如果先搜索再截断那么在内存的操作会很费时间。所以我选择直接阶段然后用工具搜索。
    2,即使我按照一个完整行必须在一个文件里,对我的日志来说也没有意义。
    因为我经常看见sql问里面就错行了。这样的化一个sql其实有的可以占好多行。到时候还是不便。
    而且读入内存的运行时间对于紧急对应实在是太长。因为一个对应可能只给你2,3个小时。
    读日志就近20分钟的话,太长了。谢谢 火龙果 甘草的指正
      

  6.   

    金山所有的网站的日志都是我的代码分析的,一般来讲,先要去粗取精一次。然后再分析。
    去粗取精,适合Python或者C++这样的程序写,逻辑不多,只是重点在IO上。
      

  7.   

    这里的日志的确很变态。备不住用的人多阿,赫赫。
    我的工作就是找错误
    vm console 页面的日志都小,sql很头疼,所以要分割一下。查了一下LineNumberReader,跟有用的一个类。
    JAVAIO 从来就没学好那位大师总体给讲一下就好了
      

  8.   

    甘草 大哥
    您说的去粗取精这个过程,现在的java和c++差距很大吗?
    另外对于本来应该在一行的数据结果不一定在一行上的情况如何处理的呢?
      

  9.   

    比如说你日志里面的IP地址,应该提炼为LONG型的,对于以后的分析有利。Java是GC的,Java表面的快速,往往是因为光new,不去delete,但是等到你内存耗费大的时候,GC就出来了,但是它的速度真的很慢。
    尽量用C++和Python脚本出来最初的log文件,把没用的东西去掉。
      

  10.   

    转自:http://blog.csdn.net/hzxdark/archive/2006/12/21/1452373.aspx  我不知道各位是师弟师妹们学java时是怎样的,就我的刚学java时的感觉,java.io包是最让我感到一头雾水的。所以现在这篇文,尽可能简单地描述java.io包的结构,希望对java.io同样一头雾水的师弟师妹们有些帮助^_^我开始学java时,java.io的介绍是在《java编程思想》里看的。说实话,当时完全看不明白——“java.io的是用‘decorator模式’来构建的”——刚学java时,天知道啥玩意叫decorator……不过要明白java.io,确实需要理解decorator设计模式,下面详细介绍下。
    所谓decorator,装饰,其实可以说是一种设计的技巧,说白了没什么难的,别看很多网上资料说的天花乱坠的(非常讨厌有些文章总是把简单的问题描述得跟两头猪的kiss问题一样复杂……)。decorator的结构如下:      MyInterface
                     |
     _______|_______
     |                                | 
    Myclass     Decorator
                    ____|_____
                    |                    | 
      DecoratorA      DecoratorB
    decorator的目的是在不改变任何原有的类的基础下,添加新的功能(你可以理解为书上说的灵活性)。其中Myclass是你要扩展的类,DecoratorA跟DecoratorB封装了你要扩展的功能,并保存有一个MyInterface的引用。考虑以下代码:
    public static void main(Strings[] arg){
      myInterface a = new myClass();
      a.print();
    }
    myInterface 是myClass的接口,只声明了一个方法print(),myClass实现了该方法:public void print(){
       System.out.println("hello");
    }那么假如我们要在不改变原来的myClass的基础上,变成输出“hello world!”,要怎么做呢?
    当然我们可以考虑直接写个myClass的子类,helloClass之类,但是要是要求根据环境不同,输出"hello world!",my hello world","my Hello"之类的组合呢?
    用继承的方式将不得不写一堆类似的子类来。decorator,装饰模式的解决方法是,只实现基本的功能,把附加的功能抽出来放一边。
    例如以下代码:
    class DecoratorA implements Decorator{
       MyInterface myObject;
       DecoratorA(myInterface myObject){
         this.myObject = myObject; 
       }
       public void print(){
             myObject.print();
             System.out.print("world!");
       }
    }class DecoratorB implements Decorator{
        MyInterface myObject;
        DecoratorA(myInterface myObject){
         this.myObject = myObject; 
        }
        public void print(){
             System.out.print("my");
             myObject.print();
        }
    }
    DecoratorA和DecoratorB的功能分别是打印出world跟my。这时main函数要打印出my hello world可简单变为:public static void main(Strings[] arg){
      MyInterface a =new DecoratorA(new DecoratorB(new MyClass());
      a.print();
    }简单吧?简单的说,就是:
    print(){
      print("xxx");//可替换成你要添加的任何处理;
      myObject.print();//调用基础类的函数;
      xxxx;        //后续处理
    }
    Decorator的介绍就到此为止,接下来讲java.io.看到MyInterface a =new DecoratorA(new DecoratorB(new MyClass());是不是觉得眼熟咧?这跟BufferedInputStream bis = new BufferedInputStream(new DataInpuStream(new FileInputStream("xxx.txt")));是不是很像?(画外音加一个臭鸡蛋扔上来:因为java.io就是用decorator模式组织的,当然像啦……)java.io分Stream跟reader、writer两大类,这里只详细介绍Stream,并最后两者间的关系。Stream又分inputStream、OutputStream,两者基本是对称的,这里也只介绍InputStream.                     java.io.InputStream
                                    |
             _______________________|________________________
               |                                              |
    ByteArrayInputStream                                     FilterInputStream
    StringBufferInputStream                  _____________________|_________________
    FileInputStream                                |                |                                        |                               |
    PipedInputStream  DataInputStream BufferedInputStream  LineNumInpuStream    XXX(注:xxx是PushbackInputStream,上面的图放不下)这个图跟最初介绍的hello world的图很像吧?呵呵。
    基础的流只有左边4个,这些流代表了数据的来源,所有的流都必须从这四个中之一开始。(注,还有一个RandomAccessFile、File,这两个不在本文介绍范围)。
    然后当我们需要什么添加功能,就从右边中选择一个装饰。例如,我们需要缓存功能,那么需要bufferedInputStream装饰:BufferdInputStream is = new BufferedInputStream(new FileInputStream("xxx.txt"));假如再要DataInputStream的功能,只要在加一层:
    DataInputStream dis = new DataInputStream(new BufferdInputStream(new FileInputStream));
    (厄,我不甚明白这个类添加的功能是做什么用的,资料说是增加读取java原生数据的功能,不甚明白,有清楚的来补充一下,pipeInputStream跟sequenceInputStream也没用过,欢迎补充说明)
    这里你可以想象成,在基本的FileInputStream.readxxx()方法在BufferedInputStream的readxxx()方法调用,并添加相应的处理。
      

  11.   

    甘草:
    我这里没有c++编译环境。Python又不会。
    vb效率如何? c应该也可以吧,还有不知道shell 直接作效率如何。
    谈谈感受吧!
      

  12.   

    File file = new File("filename"); 
    BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file),10*1024*1024); 
    // 用10M的缓冲读取
      

  13.   

    Python比较合适,这种逻辑简单,IO为主导的程序,用Python,Java,C++的耗时都差不多的。
    Python的安装太简单了,下一个,然后是下一步,下一步,下一步,下一步,下一步,完成,就可以了。
      

  14.   

    再也没有比Python更简单的语言了,一个下午的时间,足够你学会使用它,至少在这个帖子里面的问题是足够的了。
      

  15.   

    2,软件分割。免费软件不能分割大文件。可不可以用winrar来分割
      

  16.   

    看来看去,感觉研究方向偏了  :)根据楼主的问题贴,我看好像真正的目的不在于要写一个能分割文件的程序(这只是想到的方法之一),也不是真正意义上的所谓“日志分析”,而只是希望能在文本编辑器里打开文件,找到特定的内容,看一看,而已。那么我建议先试试 UltraEdit。它能不能处理 3G 的文件我没有把握,但它确实能打开“很大”的文件,关键是要在配置选项里设定“打开文件时不使用临时文件”。
      

  17.   

    TO maquan 
    的确问题最初是在不能打开大文件上。但是在用程序分割文件时想到了一些问题提出来大家讨论一下。
    ultraEdit好像需要注册的啊。即使能打开也是不好的。如果将3g文件写到内存和缓村里那么机器可能就非常慢了。
      

  18.   

    我认为这个文件根本就不要分割,直接用 BufferedReader 把指定时间段的数据写到另外一个文件里去不就得了。while(不停地找) {
        读出一行
        是否大于等于指定时间范围的下限?
            是:
                是否小于指定时间范围的上限?
                    是:写文件
                    否:停止写文件,跳出循环结束
            否:
                返回上面接着读
    }
      

  19.   

    问:火龙果
    BufferedReader 的默认缓冲区好像是8192把,如果想增加缓冲区我
    觉得用BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file),10*1024*1024);  
    比较好
      

  20.   

    To:bao110908 
    你说的方法不错。我用vb实现过类似的但感觉时间点不好定,就没怎么再用。因为grep速度还是不错的。
    发这个帖其实是有私心的,并不是单纯解决问题。因为自己对java 的 io感觉很不了解。所以想跟大家学习一下。提高自己啊。TO' taojy 
    上下翻页对调查不是很好,因为要反复看。所以还是记录下来比较好啊。我觉得。
      

  21.   


    我认为,读取文件并不需要增加缓冲区,有 8kB 差不多够用了(节约内存呗),
    当然要增加到 10MB 也可以啊,这样或许会更快一些,没有试验过。
      

  22.   

    3G 实在是太大了,竟然一天的日志就有那么多,不知道是否可以每隔一个小时换一个文件呢?
    这样 EditPlus 就可以打开一个文件了。
      

  23.   

    grep 能做的话就用这个做吧,毕竟是个 C 语言实现的原始工具,
    呵呵,Global Regular Expression Print, grep
    这个工具估计是没有办法再优化了。
      

  24.   

    一个小时一个恐怕不行的。
    现在六台机器作managed server 一小时一个的话一天就是6*24啊
    光找log也挺累得 呵呵。
      

  25.   

    不会吧,那文件名要有规律不就可以了。比如六台机器叫A、B、C、D、E、F文件名就取为 Log2008031108A.log,即 Log[yyyyMMddHH][A-F].log这样让文件名一排序不就找到了。
      

  26.   

    用java的lucene,全文检索很牛,应该能符合你的要求吧,不过3g.....还是恐怖了一点。
      

  27.   

    内存不够的时候考虑BerkerlyDB,MySQL的非Win32版本也有支持。
      

  28.   

    写诗啊? 写诗我也喜欢。我blog上也有,基本很打油。我喜欢写文章,写程序,写BLOG,打kof,打星际,打CS。写文章以抨击实政,写程序赖以为生,写blog以为藉平生,打KOF限于97,打星际限于1.08b,打CS限于1.5。
      

  29.   

    我打KOF97是很牛的,估计在程序员里面,我是打KOF最厉害的,在打KOF的人里面,我写程序是最厉害的。http://healerkx.spaces.live.com/
      

  30.   

    打KOF限于97,打星际限于1.08b,打CS限于1.5
    ________________不会玩游戏,看不懂,55555~~
      

  31.   


    [size=30px]哈哈,o(∩_∩)o[/size]
      

  32.   

    找到了一篇文章 翻译一下 大家共同学习
    如果谁看见已经有翻译好的告诉我啊 我就不写了 呵呵原文
    http://java.sun.com/developer/technicalArticles/Programming/PerfTuning/摘要翻译IO性能java总是假定只有两种文件组织形式。
    第一种,基于字节流(stream of bytes)
    第二种,基于字符序列(character sequences)java 中一个字符有两个字节。所以在从文件读字符时需要转换。提高io性能的基本原则
    1,尽量避免访问磁盘
    2,尽量避免访问OS
    3,尽量避免方法调用
    4,尽量避免单独处理字节或字符下面通过一个计算换行数的例子来说明这些原则是如何被应用的。1,读
    import java.io.*;
      
      public class intro1 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileInputStream fis =
                new FileInputStream(args[0]);
            int cnt = 0;
            int b;
            while ((b = fis.read()) != -1) {
              if (b == '\n')
                cnt++;
            }
            fis.close();
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }
    这段代码中频繁调用了OS 函数,也就是FileInputStream.read
    一个本地方法负责读取文件的下一个字节。2,采用大缓存import java.io.*;
      
     public class intro2 {
       public static void main(String args[]) {
        if (args.length != 1) {
          System.err.println("missing filename");
          System.exit(1);
        }
        try {
          FileInputStream fis =
              new FileInputStream(args[0]);
          BufferedInputStream bis =
              new BufferedInputStream(fis);
          int cnt = 0;
          int b;
          while ((b = bis.read()) != -1) {
            if (b == '\n')
              cnt++;
            }
          bis.close();
          System.out.println(cnt);
        }
        catch (IOException e) {
          System.err.println(e);
        }
      }
     }
    BufferedInputStream.read从缓存中读取字节。减少了底层api访问。3,直接缓存
    我们不使用BufferedInputStream 而是自己直接缓存。
    这样可以减少方法调用。 import java.io.*;
      
      public class intro3 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileInputStream fis =
                new FileInputStream(args[0]);
            byte buf[] = new byte[2048];
            int cnt = 0;
            int n;
            while ((n = fis.read(buf)) != -1) {
              for (int i = 0; i < n; i++) {
                if (buf[i] == '\n')
                  cnt++;
              }
            }
            fis.close();
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }对于1m的文件 执行时间分别是
     intro1    6.9
     intro2    0.9
     intro3    0.4最慢的和最快的时间比是 17 比 1当然这并不是说我们总是要采用第三种方式。
    其实第三种方式可能产生错误,特别是在处理end-of-file事件时(要仔细处理这类事件).
    并且它可读性比较差.但是作为一个方法还是应该引起我们的注意.第二种方式可能是最普通的方式.
    【缓存】
    第二种和第三种方式都用到了缓存.它确实是提高IO速度的基本手段。
    这很容易让我们想起一个问题。是不是缓存越大IO越快。
    JAVA 的缓存默认一般是1024 或 4048 比特。
    更大的缓存可能加快IO但是也就是5% 10% 左右的小幅提升。4,整个文件读取  import java.io.*;
      
      public class readfile {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            int len = (int)(new File(args[0]).length());
            FileInputStream fis =
                new FileInputStream(args[0]);
            byte buf[] = new byte[len];
            fis.read(buf);
            fis.close();
            int cnt = 0;
            for (int i = 0; i < len; i++) {
              if (buf[i] == '\n')
                cnt++;
            }
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }
     
    这样很方便但也有很明显的不足,内存是不是比文件大。另外一个缓存的关注点是向终端的输出。SYSTEM.OUT默认是行缓存,
    也就是说缓存在遇到一个换行符的时候会将内容强行输出。5,阻止行缓存
      import java.io.*;
      
      public class bufout {
        public static void main(String args[]) {
          FileOutputStream fdout =
              new FileOutputStream(FileDescriptor.out);
          BufferedOutputStream bos =
              new BufferedOutputStream(fdout, 1024);
          PrintStream ps =
              new PrintStream(bos, false);
      
          System.setOut(ps);
      
          final int N = 100000;
      
          for (int i = 1; i <= N; i++)
            System.out.println(i);
      
          ps.close();
        }
      }
     
    这个程序输出1到100000到终端他比默认情况下快三倍。【随机读写文本文件】 import java.io.*;  public class line1 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileInputStream fis =
                new FileInputStream(args[0]);
            BufferedInputStream bis =
                new BufferedInputStream(fis);
            DataInputStream dis =
                new DataInputStream(bis);
            int cnt = 0;
            while (dis.readLine() != null)
              cnt++;
            dis.close();
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }这个程序用了古老的DataInputStream.readLine来读取字符。
    新一点的方法会象下面这样 import java.io.*;  public class line2 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileReader fr = new FileReader(args[0]);
            BufferedReader br = new BufferedReader(fr);
            int cnt = 0;
            while (br.readLine() != null)
              cnt++;
            br.close();
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }
     
    这个方法会快一些。比如6m 200,000行的文件读取第二种方式可以有20%的提升。
    但即使没有提升也应该注意到一点。第一个方法会有警告,因为DataInputStream.readLine 
    这个方法没有正确变换字符,所以不要用它来处理文本文件。它可以处理ASCII的字节流。来看下面这段程序
     import java.io.*;
      
      public class conv1 {
        public static void main(String args[]) {
          try {
            FileOutputStream fos =
                new FileOutputStream("out1");
            PrintStream ps = 
                new PrintStream(fos);
            ps.println("\uffff\u4321\u1234");
            ps.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }显然他没有写我们能看懂的字符。☆Reader/Writer IO类是基于字符的,它可以来解决这类问题。
    OutputStreamWriter 是设置编码的地方。
     import java.io.*;  public class conv2 {
        public static void main(String args[]) {
          try {
            FileOutputStream fos =
                new FileOutputStream("out2");
            OutputStreamWriter osw =
                new OutputStreamWriter(fos, "UTF8");
            PrintWriter pw =
                new PrintWriter(osw);
            pw.println("\uffff\u4321\u1234");
            pw.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }
    这段程序设置编码方式为UTF8。
      

  33.   

    为什么都打KOF去了,我格斗只会月姬和GGXX也...
      

  34.   

    格式化的开销虽然写文件开销很大但数据格式化的开销也不可小视。
    看下面的三部分的例子。第一步 输出固定字符  public class format1 {
        public static void main(String args[]) {
          final int COUNT = 25000;      for (int i = 1; i <= COUNT; i++) {
            String s = "The square of 5 is 25\n";
            System.out.print(s);
          }
        }
      }
     第二步 用+简单格式化 public class format2 {
        public static void main(String args[]) {
          int n = 5;
          final int COUNT = 25000;
          for (int i = 1; i <= COUNT; i++) {
            String s = "The square of " + n + " is " +
                n * n + "\n";
            System.out.print(s);
          }
        }
      }
     第三步 用MessageFormat 格式化 import java.text.*;
      
     public class format3 {
       public static void main(String args[]) {
         MessageFormat fmt =
          new MessageFormat("The square of {0} is {1}\n");
          Object values[] = new Object[2];    int n = 5;    values[0] = new Integer(n);
        values[1] = new Integer(n * n);
      
        final int COUNT = 25000;    for (int i = 1; i <= COUNT; i++) {
          String s = fmt.format(values);
          System.out.print(s);
         }
        }
      }
     下面是输出所需的时间
     
     format1   1.3
     format2   1.8
     format3   7.8第四步 MessageFormat.format(String, Object[])   import java.text.*;
      
      public class format4 {
        public static void main(String args[]) {
          String fmt = "The square of {0} is {1}\n";
          Object values[] = new Object[2];
          int n = 5;
          values[0] = new Integer(n);
          values[1] = new Integer(n * n);
      
          final int COUNT = 25000;
          for (int i = 1; i <= COUNT; i++) {
            String s =
                MessageFormat.format(fmt, values);
            System.out.print(s);
          }
        }
      }
     
    这个代码用了上面代码的1/3的时间。
    虽然第三步费时较长但是并不代表我们不用它。
    实际上在国际化时是非常重要的。
      

  35.   

    【随机访问】
    RandomAccessFile 类用于对文件的随机访问。
    他提供了seek方法类似c/c++中的found。
    由于seek方法访问底层系统所以会比较费时。
    一个比较可行的方法是在RandomAccessFile之上
    构建自己的缓存来读取字节。例如
    import java.io.*;
      
      public class ReadRandom {
        private static final int DEFAULT_BUFSIZE = 4096;
      
        private RandomAccessFile raf;
        private byte inbuf[];
        private long startpos = -1;
        private long endpos = -1;
        private int bufsize;
      
        public ReadRandom(String name) 
         throws FileNotFoundException {
          this(name, DEFAULT_BUFSIZE);
        }
      
        public ReadRandom(String name, int b)
            throws FileNotFoundException {
          raf = new RandomAccessFile(name, "r");
          bufsize = b;
          inbuf = new byte[bufsize];
        }
      
        public int read(long pos) {
          if (pos < startpos || pos > endpos) {
            long blockstart = (pos / bufsize) * bufsize;
            int n;
            try {
              raf.seek(blockstart);
              n = raf.read(inbuf);
            }
            catch (IOException e) {
              return -1;
            }
            startpos = blockstart;
            endpos = blockstart + n - 1;
            if (pos < startpos || pos > endpos)
              return -1;
          }
      
          return inbuf[(int)(pos - startpos)] & 0xffff;
        }
      
        public void close() throws IOException {
          raf.close();
        }
      
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
      
          try {
            ReadRandom rr = new ReadRandom(args[0]);
            long pos = 0;
            int c;
            byte buf[] = new byte[1];
            while ((c = rr.read(pos)) != -1) {
              pos++;
              buf[0] = (byte)c;
              System.out.write(buf, 0, 1);
            }
            rr.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }这个策略会在你想要得到你的访问点附近的数据的时候有用。【比较】
    java 提供了类用于比较压缩的还没压缩的比特流。(java.util.zip package)
    下面的代码接受一个文件输入,然后写到一个压缩文件里。import java.io.*;
     import java.util.zip.*;
      
      public class compress {
        public static void doit(
                                String filein, 
                                String fileout
                                ) {
          FileInputStream fis = null;
          FileOutputStream fos = null;
          try {
            fis = new FileInputStream(filein);
            fos = new FileOutputStream(fileout);
            ZipOutputStream zos =
                new ZipOutputStream(fos);
            ZipEntry ze = new ZipEntry(filein);
            zos.putNextEntry(ze);
            final int BUFSIZ = 4096;
            byte inbuf[] = new byte[BUFSIZ];
            int n;
            while ((n = fis.read(inbuf)) != -1)
              zos.write(inbuf, 0, n);
            fis.close();
            fis = null;
            zos.close();
            fos = null;
          }
          catch (IOException e) {
            System.err.println(e);
          }
          finally {
            try {
              if (fis != null)
                fis.close();
              if (fos != null)
                fos.close();
            }
            catch (IOException e) {
            }
          }
        }
      public static void main(String args[]) {
        if (args.length != 2) {
         System.err.println("missing filenames");
         System.exit(1);
        }
       if (args[0].equals(args[1])) {
         System.err.println("filenames are identical");
         System.exit(1);
          }
          doit(args[0], args[1]);
        }
      }下面的程序用于解压
    import java.io.*;
     import java.util.zip.*;
      
      public class uncompress {
        public static void doit(
                                String filein, 
                                String fileout
                                ) {
          FileInputStream fis = null;
          FileOutputStream fos = null;
          try {
            fis = new FileInputStream(filein);
            fos = new FileOutputStream(fileout);
            ZipInputStream zis = new ZipInputStream(fis);
            ZipEntry ze = zis.getNextEntry();
            final int BUFSIZ = 4096;
            byte inbuf[] = new byte[BUFSIZ];
            int n;
            while ((n = zis.read(inbuf, 0, BUFSIZ)) != -1)
              fos.write(inbuf, 0, n);
            zis.close();
            fis = null;
            fos.close();
            fos = null;
          }
          catch (IOException e) {
            System.err.println(e);
          }
          finally {
            try {
              if (fis != null)
                fis.close();
              if (fos != null)
                fos.close();
            }
            catch (IOException e) {
            }
          }
        }
        public static void main(String args[]) {
          if (args.length != 2) {
         System.err.println("missing filenames");
         System.exit(1);
          }
        if (args[0].equals(args[1])) {
         System.err.println("filenames are identical");
         System.exit(1);
          }
          doit(args[0], args[1]);
        }
      }压缩是否有助于提升io性能很大程度上拒绝与你的硬件设置,特别是cpu和硬盘。
    zip压缩技术可以减少50%的数据量,但需要时间压缩和解压缩。
    压缩常见的应用是往低速设备上(软盘)写入。它可以节约50%的时间。
      

  36.   


    【缓存】
    详细的硬件缓存讨论超出了本文范围。但是有时软件的缓存可以提升io.
    考虑这样一个情况。你想以随机方式读入一个文本文件。
    一个方法是读所有的行存入arraylist. import java.io.*;
     import java.util.ArrayList;
      
      public class LineCache {
        private ArrayList list = new ArrayList();
      
        public LineCache(String fn) throws IOException {
          FileReader fr = new FileReader(fn);
          BufferedReader br = new BufferedReader(fr);
          String ln;
          while ((ln = br.readLine()) != null)
            list.add(ln);
          br.close();
        }
      
        public String getLine(int n) {
          if (n < 0)
            throw new IllegalArgumentException();
      
          return (n < list.size() ? 
           (String)list.get(n) : null);
        }
      
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            LineCache lc = new LineCache(args[0]);
            int i = 0;
            String ln;
            while ((ln = lc.getLine(i++)) != null)
              System.out.println(ln);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      } 
    getLine 方法用于提取行.这很常用但耗费大量内存.
    一个改进的方法是记住最近被请求的100行.Tokenization(不会翻译)
    Tokenization把字节或者字符序列分成逻辑快 比如词. import java.io.*;
      public class token1 {
        public static void main(String args[]) {
         if (args.length != 1) {
           System.err.println("missing filename");
           System.exit(1);
          }
          try {
            FileReader fr = new FileReader(args[0]);
            BufferedReader br = new BufferedReader(fr);
            StreamTokenizer st = new StreamTokenizer(br);
            st.resetSyntax();
            st.wordChars('a', 'z');
            int tok;
            while ((tok = st.nextToken()) !=
                StreamTokenizer.TT_EOF) {
              if (tok == StreamTokenizer.TT_WORD)
                ;// st.sval has token
            }
            br.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }
    同样的功能
    import java.io.*;
      public class token2 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileReader fr = new FileReader(args[0]);
            BufferedReader br = new BufferedReader(fr);
            int maxlen = 256;
            int currlen = 0;
            char wordbuf[] = new char[maxlen];
            int c;
            do {
              c = br.read();
              if (c >= 'a' && c <= 'z') {
                if (currlen == maxlen) {
                  maxlen *= 1.5;
                  char xbuf[] =
                      new char[maxlen];
                  System.arraycopy(
                      wordbuf, 0,
                      xbuf, 0, currlen);
                  wordbuf = xbuf;
                }
                wordbuf[currlen++] = (char)c;
              }
              else if (currlen > 0) {
                String s = new String(wordbuf,
                    0, currlen);
              // do something with s
                currlen = 0;
              }
            } while (c != -1);
            br.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }
    第二个比第一个快20%.
      

  37.   

    【序列化】
    下边的例子输出一串随机数
    import java.io.*;
      import java.util.*;
      
      public class serial1 {
        public static void main(String args[]) {
          ArrayList al = new ArrayList();
          Random rn = new Random();
          final int N = 100000;
      
          for (int i = 1; i <= N; i++)
            al.add(new Integer(rn.nextInt()));
      
          try {
            FileOutputStream fos =
                new FileOutputStream("test.ser");
            BufferedOutputStream bos =
                new BufferedOutputStream(fos);
            ObjectOutputStream oos =
                new ObjectOutputStream(bos);
            oos.writeObject(al);
            oos.close();
          }
          catch (Throwable e) {
            System.err.println(e);
          }
        }
      }
    读入arraylist
     import java.io.*;
     import java.util.*;
      
      public class serial2 {
        public static void main(String args[]) {
          ArrayList al = null;
      
          try {
            FileInputStream fis =
                new FileInputStream("test.ser");
            BufferedInputStream bis =
                new BufferedInputStream(fis);
            ObjectInputStream ois =
                new ObjectInputStream(bis);
            al = (ArrayList)ois.readObject();
            ois.close();
          }
          catch (Throwable e) {
            System.err.println(e);
          }
        }
      }
    这里我们都用了缓存.
    是不是有更快的方法来序列化输出大量数据呢.很可能没有.
    比如.你决定写64比特的长整数而不是8个比特.长整数的最大长度
    大约是20个字符.是二进制表示长度的2.5倍.所以这种格式不会再快了.
    但在特殊情况下,比如bitmaps可能情况会不一样。除了实际的io ,序列化的格式化开销(在DataInputStream和DataOutputStream中),
    还有其他的开销,比如反序列化。比如
     import java.io.*;
      import java.util.*;
      
      public class binary1 {
        public static void main(String args[]) {
          try {
            FileOutputStream fos =
                new FileOutputStream("outdata");
            BufferedOutputStream bos =
                new BufferedOutputStream(fos);
            DataOutputStream dos =
                new DataOutputStream(bos);
            Random rn = new Random();
            final int N = 10;
            dos.writeInt(N);
            for (int i = 1; i <= N; i++) {
              int r = rn.nextInt();
              System.out.println(r);
              dos.writeInt(r);
            }
            dos.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    import java.io.*;
      
      public class binary2 {
        public static void main(String args[]) {
          try {
            FileInputStream fis =
                new FileInputStream("outdata");
            BufferedInputStream bis =
                new BufferedInputStream(fis);
            DataInputStream dis =
                new DataInputStream(bis);
            int N = dis.readInt();
            for (int i = 1; i <= N; i++) {
              int r = dis.readInt();
              System.out.println(r);
            }
            dis.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }【获取文件信息】
    到现在为止我们提到的都是IO.但还有一个方面值得我们注意,那就是关于获取文件信息的操作。
     import java.io.*;
      
      public class length1 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          File f = new File(args[0]);
          long len = f.length();
          System.out.println(len);
        }
      }
     
    JAVA 虚拟机自己并不知道文件的长度所以必须去调用系统函数。
    对于文件的其他属性也一样,比如文件的更新时间等。另外一个例子 import java.io.*;
      
      public class roots {
        public static void visit(File f) {
          System.out.println(f);
        }
      
        public static void walk(File f) {
          visit(f);
          if (f.isDirectory()) {
            String list[] = f.list();
            for (int i = 0; i < list.length; i++)
              walk(new File(f, list[i]));
          }
        }
      
        public static void main(String args[]) {
          File list[] = File.listRoots();
          for (int i = 0; i < list.length; i++) {
            if (list[i].exists())
              walk(list[i]);
            else
              System.err.println("not accessible: "
                  + list[i]);
          }
        }
      }
    他用了File 的方法比如isDirectory 和 exists。
    每个文件仅仅访问一次。