2012-06-19 31 views
6

Tôi có một tập tin rất lớn với hàng triệu cột, tách ra bởi không gian, nhưng nó chỉ có một số hạn chế về hàng:Làm thế nào để đọc cột thứ hai trong một file lớn

examples.txt:

1 2 3 4 5 ........ 
3 1 2 3 5 ......... 
l 6 3 2 2 ........ 

Bây giờ, tôi chỉ muốn đọc trong cột thứ hai:

2 
1 
6 

làm thế nào để làm điều đó trong java với hiệu suất cao.

Cảm ơn

Cập nhật: tệp thường là 1,4G chứa hàng trăm hàng.

+0

Mỗi hàng có chứa cùng số ký tự không? – cheeken

+0

không thực sự ... – Frank

+0

Tôi bị mất. Định dạng 1 chữ số có theo sau là 1 dấu cách, v.v. với cùng số ký tự trên mỗi dòng không? – Gene

Trả lời

2

Nếu tệp của bạn không được cấu trúc tĩnh, tùy chọn duy nhất của bạn là tùy chọn ngây thơ: đọc qua chuỗi byte tệp theo chuỗi byte tìm kiếm dòng mới và lấy cột thứ hai sau mỗi dòng. Sử dụng FileReader.

Nếu tệp của bạn được cấu trúc tĩnh, bạn có thể tính toán vị trí trong tệp cột thứ hai cho một dòng nhất định và seek() trực tiếp.

+2

làm net đọc từng dòng ... chỉ cần đọc rất nhiều byte và lặp lại nó .. nếu dòng dài, bạn chặn một thời gian dài trong khi đọc và ram là đầy đủ của nó! – headgrowe

+0

Tôi không chắc chắn ý của bạn là gì. Ông đã khá rõ ràng trong việc nói đọc _by byte_ tìm kiếm ký tự dòng mới, không phải bằng dòng. – Gene

+0

vâng, tôi muốn cụ thể hơn .. – headgrowe

0

Đây là một máy trạng thái nhỏ sử dụng FileInputStream làm đầu vào và xử lý bộ đệm của riêng nó. Không có chuyển đổi ngôn ngữ.

Trên máy tính xách tay 1,4 GHz 7 tuổi của tôi với 1/2 GB bộ nhớ mất 48 giây để đi qua 1,28 tỷ byte dữ liệu. Bộ đệm lớn hơn 4Kb dường như chạy chậm hơn.

Trên một chiếc MacBook cũ 1 năm mới có 4Gb chạy trong 14 giây. Sau khi tệp được lưu trong bộ nhớ cache, nó sẽ chạy trong 2,7 giây. Một lần nữa không có sự khác biệt với bộ đệm lớn hơn 4Kb. Đây là cùng một tệp dữ liệu 1,2 tỷ byte.

Tôi mong đợi IO được ánh xạ bộ nhớ sẽ làm tốt hơn, nhưng điều này có lẽ là di động hơn.

Nó sẽ tìm nạp bất kỳ cột nào bạn yêu cầu.

import java.io.*; 
import java.util.Random; 

public class Test { 

public static class ColumnReader { 

    private final InputStream is; 
    private final int colIndex; 
    private final byte [] buf; 
    private int nBytes = 0; 
    private int colVal = -1; 
    private int bufPos = 0; 

    public ColumnReader(InputStream is, int colIndex, int bufSize) { 
     this.is = is; 
     this.colIndex = colIndex; 
     this.buf = new byte [bufSize]; 
    } 

    /** 
    * States for a tiny DFA to recognize columns. 
    */ 
    private static final int START = 0; 
    private static final int IN_ANY_COL = 1; 
    private static final int IN_THE_COL = 2; 
    private static final int WASTE_REST = 3; 

    /** 
    * Return value of colIndex'th column or -1 if none is found. 
    * 
    * @return value of column or -1 if none found. 
    */ 
    public int getNext() { 
     colVal = -1; 
     bufPos = parseLine(bufPos); 
     return colVal; 
    } 

    /** 
    * If getNext() returns -1, this can be used to check if 
    * we're at the end of file. 
    * 
    * Otherwise the column did not exist. 
    * 
    * @return end of file indication 
    */ 
    public boolean atEoF() { 
     return nBytes == -1; 
    } 

    /** 
    * Parse a line. 
    * The buffer is automatically refilled if p reaches the end. 
    * This uses a standard DFA pattern. 
    * 
    * @param p position of line start in buffer 
    * @return position of next unread character in buffer 
    */ 
    private int parseLine(int p) { 
     colVal = -1; 
     int iCol = -1; 
     int state = START; 
     for (;;) { 
      if (p == nBytes) { 
       try { 
        nBytes = is.read(buf); 
       } catch (IOException ex) { 
        nBytes = -1; 
       } 
       if (nBytes == -1) { 
        return -1; 
       } 
       p = 0; 
      } 
      byte ch = buf[p++]; 
      if (ch == '\n') { 
       return p; 
      } 
      switch (state) { 
       case START: 
        if ('0' <= ch && ch <= '9') { 
         if (++iCol == colIndex) { 
          state = IN_THE_COL; 
          colVal = ch - '0'; 
         } 
         else { 
          state = IN_ANY_COL; 
         } 
        } 
        break; 

       case IN_THE_COL: 
        if ('0' <= ch && ch <= '9') { 
         colVal = 10 * colVal + (ch - '0'); 
        } 
        else { 
         state = WASTE_REST; 
        } 
        break; 

       case IN_ANY_COL: 
        if (ch < '0' || ch > '9') { 
         state = START; 
        } 
        break; 

       case WASTE_REST: 
        break; 
      } 
     } 
    } 
} 

public static void main(String[] args) { 
    final String fn = "data.txt"; 
    if (args.length > 0 && args[0].equals("--create-data")) { 
     PrintWriter pw; 
     try { 
      pw = new PrintWriter(fn); 
     } catch (FileNotFoundException ex) { 
      System.err.println(ex.getMessage()); 
      return; 
     } 
     Random gen = new Random(); 
     for (int row = 0; row < 100; row++) { 
      int rowLen = 4 * 1024 * 1024 + gen.nextInt(10000); 
      for (int col = 0; col < rowLen; col++) { 
       pw.print(gen.nextInt(32)); 
       pw.print((col < rowLen - 1) ? ' ' : '\n'); 
      } 
     } 
     pw.close(); 
    } 

    FileInputStream fis; 
    try { 
     fis = new FileInputStream(fn); 
    } catch (FileNotFoundException ex) { 
     System.err.println(ex.getMessage()); 
     return; 
    } 
    ColumnReader cr = new ColumnReader(fis, 1, 4 * 1024); 
    int val; 
    long start = System.currentTimeMillis(); 
    while ((val = cr.getNext()) != -1) { 
     System.out.print('.'); 
    } 
    long stop = System.currentTimeMillis(); 
    System.out.println("\nelapsed = " + (stop - start)/1000.0); 
} 
} 
+0

như tôi buồn "làm net đọc từng dòng ... chỉ cần đọc rất nhiều byte và lặp lại nó .. nếu dòng dài, bạn chặn một thời gian dài trong khi đọc và ram đầy nó! " ... bằng cách này, một số nguyên dài 4 byte ... do đó bạn có thể lưu hàng mà không có dấu cách và không giống như chuỗi ... việc đọc mà không chuyển đổi thành chuỗi thực sự nhanh hơn ..... sử dụng FileInputStream ... – headgrowe

+0

Chúng tôi đang trong thỏa thuận bạo lực. Tôi đã viết để thử BufferedReader và getLine trước khi ông đăng kích thước thật của tập tin. Nó không bao giờ tốt để làm tối ưu hóa mã khó khăn trước khi bạn chắc chắn rằng họ đang cần thiết. – Gene

0

Tôi phải đồng ý với @gene, hãy thử với BufferedReader và getLine trước, thật đơn giản và dễ dàng để mã. Chỉ cần cẩn thận không để bí danh mảng sao lưu giữa kết quả của getLine và bất kỳ thao tác chuỗi con nào mà bạn sử dụng. String.substring() là một thủ phạm đặc biệt phổ biến, và tôi đã có các mảng byte đa MB bị khóa trong bộ nhớ vì một chuỗi con 3-char đã tham chiếu nó.

Giả sử ASCII, tùy chọn của tôi khi thực hiện việc này là giảm xuống mức byte. Sử dụng mmap để xem tệp dưới dạng ByteBuffer và sau đó thực hiện quét tuyến tính cho 0x20 và 0x0A (giả sử dải phân cách kiểu unix). Sau đó chuyển đổi các byte có liên quan thành một String. Nếu bạn đang sử dụng một bộ ký tự 8-bit thì cực kỳ khó để nhanh hơn.

Nếu bạn đang sử dụng Unicode, vấn đề phức tạp hơn và tôi khuyên bạn nên sử dụng BufferedReader trừ khi hiệu suất đó thực sự là không thể chấp nhận. Nếu getLine() không hoạt động, thì hãy xem xét chỉ lặp lại cuộc gọi đến read().

Bất kể bạn nên luôn chỉ định Bộ ký tự khi khởi tạo chuỗi từ luồng bên ngoài. Điều này giải thích rõ ràng giả định bộ ký tự của bạn.Vì vậy, tôi khuyên bạn nên sửa đổi nhỏ đối với đề xuất của gen, do đó, một trong số:

int i = Integer.parseInt(new String(buffer, start, length, "US-ASCII")); 

int i = Integer.parseInt(new String(buffer, start, length, "ISO-8859-1")); 

int i = Integer.parseInt(new String(buffer, start, length, "UTF-8")); 

nếu thích hợp.

Các vấn đề liên quan