用Java读文件实现这么简单的要求都不行:
我要读一大文件,300万行,我想将文件指针移动到指定行,读取指定内容。一句话,就是读取指定行的内容。
看了很多API,都没有该功能,RandomAccessFile ,LineNumberReader等类都没用,最多能实现的是,跳过N个字符。
特别是LineNumberReader类,不知道有什么用,骗人的setLineNumber方法,根本无法改变文件指针。大家有什么方案吗

解决方案 »

  1.   

    JSR(Java 规范请求)是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR(Java 规范请求),以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。楼主可以试试
      

  2.   

    我现在想了个办法,就是让文件每行都一样多的数据,这样利用seek方法就可以间接实现跳到指定行,不过插入更新数据的时候就不爽了,麻烦。
      

  3.   

    我之所以没有选择数据库,就是想自己写文件可能速度快,因为数据库也是写文件。直接写文件可能效率高,但是JAVA没有这个功能好麻烦,如果数据库也这么循环读的话,那不慢死了
      

  4.   

    LineNumberReader这个类真有点忽悠人
      

  5.   

    这个文件格式你是否可以适当修改?
    把一个大文件拆分成若干小文件,比如1000行一个,文件名后缀代表开始位置。
    然后读某一特定行的时候,先打开对应的文件,然后跳过相应行数(用readLine())
      

  6.   

    晕,就是那个1ms的问题?
    你这个方案很不好,我想速度能达到1S就不错了。
      

  7.   

    哈哈,楼上的兄弟,也是搞JAVA的吗?我本来想在那边问,但是我怕那边是MYSQL板块。
      

  8.   

    不错,我也这么想的,这其实就是类似数据库索引的机制,像MYSQL的2进制日志文件就是这么处理的,不过1000行一个文件的话,那我不是要3000个文件。是否太多了。
      

  9.   


    jAVA里的readLine()只是读取某行的数据,没法实现跳过多少行????只能跳过多少字节。
      

  10.   

    行?
    只不过是按换行符分隔而已.读第几行本质上和读第几个'a'也没有区别.能跳过byte或char难道不是提供了跳过行?java没有直接API,其他语言有?除了ed那种行处理程序,好像都不会有吧?
    要不,lz用shell吧,shell干这个事情比java搞效得多.
      

  11.   

    再重复一遍,为了达到1MS的目地,直接用Disk I/O绝对会让你失望。首先,不要完全通过\r\n来分割数据。所有高效的I/O处理,都只有跳过多上字节,而没有跳过多少行。比如在“每行”(从这里开始,“行”指数据行,并非文件行)开始,固定2个字节代表该行长度,也就是跳过一行需要跳过多少字节。
    然后,再外部做一个行索引,保留每1000行的起始位置。还有一种方案,固定数据行长度,也就是VARCHAR->CHAR,以空间换速度(MySQL里面也可以试试此招,不知在读取数据时,是否会更快)
      

  12.   


    我主要是想借鉴数据库读文件的方法,数据库读文件那么高效,JAVA要可以达到那样的效率就好了。
      

  13.   

    数据库不可能把几百万条数据存放到很多文件的,看看MYSQL就知道了,MYISAM是一个表一个数据文件,其他数据库类似。
      

  14.   


    shell 不可能高效的。程序(不管java/c)可以保持输入流。
      

  15.   

    一次跳n行,如果有那个API,那么一定知道怎么去识别跳过了几行,两种方法,顺序读,读了几行就是几行,另一种就是索引,把每一行或每几行开头记录到索引。
    前者需要顺序读,效率不会高,后者能提高效率,但是需要自己建索引。
      

  16.   


    我是说用ed或awk之类的行编辑软件.
      

  17.   

    这个我赞成,不过如果要研究的话,要用底层开发语言来实现才行,直接用JAVA可能就不行。
      

  18.   

    其实文件说白了 也只是一种数据载体 但是载体的形式是千变万化的 而java的io处理 是从最基本的流入手来实现的 因此针对你这样特定的问题 是很有可能没有针对性的办法但是从另一方面来看 既然文件的形式可以千变万化 那么如何组织这个文件 就是有学问的了 数据库访问大数据量的数据很快 是因为数据库是一种典型的空间换时间的设计 如果你能把文件组织的像数据库那样高效 那访问速度自然也就提高了很多
      

  19.   


    HSQLDB, Derby(Java DB) 这些都是使用纯 Java 实现的关系型数据库。
      

  20.   

    他说的应该就是RandomAccessFile类
      

  21.   

    hsqldb可不是基于二进制的数据库引擎
    它基于的是sql语句,虽然号称可以支持8G的存储,可实际上连基本的事务控制都没有。
    拿hsqldb来说事情,可以说明java确实可以写小小数据库,但是和这里的需求没太大关系哦,有点跑题了。
      

  22.   

    RandomAccessFile这个类不行,只能跳过多少个字节,不能跳过多少行。
      

  23.   

    问题是,文件格式既然没有额外的信息,你也不知道里边到底第几行的偏移值是多少,所以只能循环。
    就算谁写了一个可以跳过去的方法,里边的实现也一样是循环过去。何苦呢,与其要求底层提供不可能的功能,不如从本源上考虑一下解决方案,现在不是又兴起一种叫nosql的日志分析技术么?
      

  24.   

    太死脑筋了,java不提供的功能就不能自己实现一个?
    完全可以自己实现一套这类的底层库。单是考虑\r\n, \n就够麻烦了,
    首先遍历整个文件,动态生成index索引,记录每一行的偏移,这样就简单多了。想要哪一行直接从索引里找就行。
      

  25.   

    数据库能实现insert,update,select 1毫秒以下吗,我测试了,300万行的数据,大部分情况都要1ms的?不过文件也很难实现?
      

  26.   

    检索速度的提高都是靠索引的。
    数据库也是索引,全文检索也是索引。它们都是从文件存储格式上下工夫。首先水平分区存储数据,别300w行进一个文件。
    然后建立索引文件,加快搜索速度。update操作就要靠文件算法了。上头已经有人提到过,顺序存储的结构,在中间进行插入更新,删除,都会造成后面所有部分的内容发生变化。想提高更新速度,最佳选择就是实现链表。但是链表又会导致搜索的难度加大。这些东西都整合在一起,可以说实现一个通用的工具难度有点儿大。但是一般数据本身都有会自己的规则,善加利用可以提高不少效率。
      

  27.   

    考虑文件的话,先从io和数据结构两方面考虑,不能直接说java有缺陷吧?
    呵呵~加油。
      

  28.   

    1ms基本上不可能,I/O读写的时间数量级就是毫秒级,能做5ms已经很不错了,1ms以下除非硬盘读写技术发生革命性更新。
      

  29.   

    抛砖引玉一下,没有做任何异常、边界处理
    public class Test {  // 数据流
      static RandomAccessFile data;
      // 索引流
      static RandomAccessFile index;  public static void main(String[] args) throws Exception {
        try {
          open();
          // 第一次初始化数据
          // init();
          long start = System.currentTimeMillis();
          for (int i = 0; i < 1000; i++) {
            System.out.println(read(100));
            System.out.println(read(1000));
            System.out.println(read(10000));
          }
          long end = System.currentTimeMillis();
          System.out.println(end - start);
        } finally {
          close();
        }
      }  static String read(int idx) throws Exception {
        long indexPos = idx << 3;
        index.seek(indexPos);
        long dataPos = index.readLong();
        long nextDataPos = index.readLong();
        data.seek(dataPos);
        int length = (int) (nextDataPos - dataPos) >> 1; // pos: bytes -> length: chars
        char[] buff = new char[length];
        for (int i = 0; i < buff.length; i++) {
          buff[i] = data.readChar();
        }
        return new String(buff);
      }  static void init() throws Exception {
        for (int i = 0; i < 100000; i++) {
          index.writeLong(data.getFilePointer());
          data.writeChars("No. " + i);
        }
      }  static void open() throws Exception {
        data = new RandomAccessFile("D:/test/test.dat", "rw");
        index = new RandomAccessFile("D:/test/test.idx", "rw");
      }  static void close() throws Exception {
        if (data != null) {
          try {
            data.close();
            data = null;
          } catch (Exception ex) {
          }
        }
        if (index != null) {
          try {
            index.close();
            index = null;
          } catch (Exception ex) {
          }
        }
      }
    }
      

  30.   

    所有的时候采用lucene之类的全文检索类的index应该没啥问题。问题是update时,想当然以为添加一行很简单,太天真了。
      

  31.   

    文件本来就没有行的概念!
    如果非要加入行的概念,你就得自己扩展File类了。
      

  32.   

    那个是扩展File类吗???好像在说:我要一个只有月日的Date,由于这些Date全部放在List中,我应当扩展List类。特别是你的Varchar字段本身存在换行的情况下。
      

  33.   

    刚才,试了一个300w行,每行平均长度3000字节(1500字符),总数据量8.44G的数据文件,
    分别读取第100行、1,000、10,000、1,000,000行,循环1000遍,平均每行处理时间8ms而且,用文件的话,似乎必须使用固定长度,否则update操作涉及到长度变化时将很麻烦。本来lucene也是一个方案。但是据LZ原帖讲,读写比很低。怕帮不了什么忙。
    于事无补,反而添乱
      

  34.   

    正好在考虑类似问题,准备这几天做。最后还是要自己建立一个index把各行Position,Length记录下来
    index可以是动态的,随实际dat自动增长。假设该方法为readLine(intLineId)
    0、查看index是否存在?不在转10,存在转20
    10、若index不存在,遍历dat,建立index,转步骤999读取
    20、检查index中标识(data,size)是否和dat同步?同步转999读取,不在转步骤30
    30、获取index的最后一行,再次基础上遍历data,重建index,转步骤999
    999、读取index.getLine(intLineId),根据line.Offset,Line,Length用RandomAccessFile读取文件这里Index可以用数组,Serialize保存
    如果多线程,考虑到同步,需要确保只有一个Index实例
    class CLine
      public int Offset,Length;
    end class
    class CIndex
      private static HashMap<File,CIndex> m_hmOpenIndex=new HashMap<..>();
      private int ma_intOffset[],ma_intLength[];
      private CIndex(){}
      public CIndex open(File fle){
        if (m_hmOpenIndex.containsKey(fle)) return m_hmOpenIndex.get(fle);
        //初始化一个新类
        ma_intOffset=new int[3000000];  //效率原因,使用数组而不是vector,如果行非常多,就不用serialize,直接把index用io随机读写,但这样同步问题就很难处理了。
        ma_intLength=new int[3000000];
        scan(fle);
        .......
      }
      public getLine(int intId){
         return newLine(ma_intOffset[intId],ma_intLength[intId]);
      }
    end class
      

  35.   

    写文件的时候,我是一条数据readLine()一下,所以就有了行的概念,这里的行也就是数据行。
    现在想想,确实没有必要记录这个数据行,因为方法调用者也不可能知道这个行号。考虑这个方案:在写文件的时候,把当前的位置(第几个字节),和该条数据的KEY,记录到索引文件,下次读的时候,知道了KEY,根据KEY从索引文件找到位置信息,再根据位置信息从数据文件直接定位到要读的数据。其实索引文件也要300万行数据,虽然只记录2个字段,还是慢,还是没有解决性能问题。因此这个方案不可行。
      

  36.   

    我测试了下,用文件这样处理,读取的性能跟数据库差不多,比数据库还差点。
    我用MySQL SELECT 1000条 1000ms左右就搞定了。(如果有查询缓存只要600多ms,这里不适合用查询缓存,更新频繁),刚用文件读1000条,基本上要1500-2000ms.
      

  37.   


    只要Offset和Length大小固定,索引可以用RandomAccessFile解决啊。
    这样你的开销只要IO就可以了,一般Os都有文件缓存,实际只会更快。
    但是同步更新之类比纯载入内存麻烦。
    class CPosition
       public int Offset,Length
       public final static Size=8;
    end class  class CIndex
       public CPostion get(int intLine){
          raf.seek(intLine * CPosition.Size);
          return new CPosition(raf.readInt(),raf.readInt());
       }
    end class
      

  38.   

    建议LZ去看下MYSQL的开源数据文件结构,记的是C语言版本的,
    替你更正一下:高性能数据库(如ORACLE等)是绕过操作系统和文件系统直接读写硬盘的!
    你所用的JAVA I/O流还是基于封装的,而且还得过操作系统和文件系统(取决于操作平台),不懂JAVA,搞C/C++的飘过...
      

  39.   

    300w还好吧,具体处理视你的文件数据,用RandomAccessFile 绝对可以(比如读取每行的时候让seek自增行长,然后将行长维护起来,可以维护到文件方便下次使用,到时候想读哪里就读哪里),但是非常的低效,如果是规则型的数据,使用其他的流也没有问题,如果是无规则的,那只能自己适当重写部分方法
    按照搂主描述,既然还要对文件插入和删除,那规则型的数据可能性比较大,如果能使用数据库,那自然是应该使用数据库的了
      

  40.   

    300w小kiss啊,估计代码和架构写得不好
      

  41.   

    抱歉,开始不仔细,没照顾到读写比小的问题。update频繁,就要考虑hash散列和链表结构了,顺序结构的效率没法保证这么高。
      

  42.   

    不要怪java,是你自己的要求太无理。
    如果每一行的长度都一样的话,自己算一下就知道从什么地方开始读。
    如果每一行的长度不一样的话,就算是神仙也不可能一下子就知道第5000行从什么地方开始。除非一行行地读,别无他法。你可以建一个数组,存放每一行的起始位置,当然要遍历一遍整个文件。LineNumberReader类本来就不是给你用的,是jdk自用的一个类,可以在发生异常的时候报告错误发生在源文件的第几行。
      

  43.   

    需求是:添加数据,更新数据和读取数据要分别要在1ms内完成,读取都是根据KEY查询的,很简单的查询,如果用数据库,类似select * from mytable where key = ?这样的语句。我用数据库测试了,基本上在1-2ms之间,数据库还是在本地,数据量170多万,所以达不到要求,在研究其他办法。
      

  44.   

    听说好多用mysql的公司为了提高其性能,都修改其源代码,比如腾讯好像是这样。
      

  45.   

    但是修改MYSQL源代码的,投入成本相对高,一般公司都搞不起,而且对技术能力要求还是比较高,起码要先理解别人的设计。理解总体设计思路不困难,但是要理解其代码,还是需要时间的,而且不是简单的修改,是优化性能。总之,工程量比较大。
      

  46.   

    建了索引的,最多查询勉强可以达到1ms,加上查询缓存到可以达到500ms查询1000条,但是因为每次查询后一般都要update,所以查询缓存效果不佳,另外update基本无法达到1ms。如果一个事务要求:先select,然后update,这个事务的时间就超过了1ms. 主要是现在的数据量才170多万,而且数据库在本地,如果数据量300万,数据库不在本地,可能表现更差。我总体上研究了下,查询同步处理,更新异步处理可能有效,但是数据一致性处理起来较为复杂
      

  47.   

    谁知道 mysql工具 show profiles 显示的duration的时间的单位是什么,秒,毫秒,微妙,皮秒,飞秒??我搜索了半天没这个说明,找到的都是使用方法。
      

  48.   

    建议Lz用hadoop吧,一般文件存储都不建议update,delete,一个update操作将整个文件都lock,多方update,delete那更不用说
      

  49.   

    绝不可能符合LZ需要,LZ不是为了解决海量数据的存储,而是为了解决300万左右数据1ms内SELECT/UPDATE
      

  50.   

    总体的性能,select ,update,能达到0.8ms就解决我的问题了。