2009-06-25 85 views
3

有沒有辦法用BufferedReader讀取ByteBuffer,而不必首先將它變成String?我想通讀一個相當大的ByteBuffer作爲文本行,並且出於性能原因,我想避免將它寫入磁盤。在ByteBuffer上調用toString不起作用,因爲結果字符串太大(它會拋出java.lang.OutOfMemoryError:Java堆空間)。我原以爲API中會有東西將一個ByteBuffer包裝在合適的閱讀器中,但我似乎無法找到任何合適的東西。用於大型ByteBuffer的BufferedReader?

下面是一個簡短的代碼示例說明了我在做什麼):

// input stream is from Process getInputStream() 
public String read(InputStream istream) 
{ 
    ReadableByteChannel source = Channels.newChannel(istream); 
    ByteArrayOutputStream ostream = new ByteArrayOutputStream(bufferSize); 
    WritableByteChannel destination = Channels.newChannel(ostream); 
    ByteBuffer buffer = ByteBuffer.allocateDirect(writeBufferSize); 

    while (source.read(buffer) != -1) 
    { 
    buffer.flip(); 
    while (buffer.hasRemaining()) 
    { 
     destination.write(buffer); 
    } 
    buffer.clear(); 
    } 

    // this data can be up to 150 MB.. won't fit in a String. 
    result = ostream.toString(); 
    source.close(); 
    destination.close(); 
    return result; 
} 

// after the process is run, we call this method with the String 
public void readLines(String text) 
{ 
    BufferedReader reader = new BufferedReader(new StringReader(text)); 
    String line; 

    while ((line = reader.readLine()) != null) 
    { 
    // do stuff with line 
    } 
} 

回答

5

它爲什麼你使用一個字節的緩衝區下手目前尚不清楚。如果你有一個InputStream,你想讀它的線,爲什麼你不只是使用BufferedReader包裝InputStreamReader?獲得NIO參與的好處是什麼?

調用上一個ByteArrayOutputStream聽起來像一個壞主意toString()我,即使你必須爲它的空間:更好地得到它作爲一個字節數組,敷在ByteArrayInputStream,然後一個InputStreamReader,如果你真的必須有一個ByteArrayOutputStream。如果你真的想要調用toString(),至少要使用重載,它使用字符編碼的名稱來使用 - 否則它將使用系統默認值,這可能不是你想要的。

編輯:好的,所以你真的想使用NIO。最終你還是在寫信給ByteArrayOutputStream,所以你最終會得到一個包含數據的BAOS。如果你希望避免的數據副本,則需要從ByteArrayOutputStream派生,比如像這樣:

public class ReadableByteArrayOutputStream extends ByteArrayOutputStream 
{ 
    /** 
    * Converts the data in the current stream into a ByteArrayInputStream. 
    * The resulting stream wraps the existing byte array directly; 
    * further writes to this output stream will result in unpredictable 
    * behavior. 
    */ 
    public InputStream toInputStream() 
    { 
     return new ByteArrayInputStream(array, 0, count); 
    } 
} 

然後你就可以創建輸入流,把它包在一個InputStreamReader,包裹在一個BufferedReader,你就不在了。

+0

好問題 - 我會同意,這就是我會做什麼如果我有選擇的話。我不能在這種情況下的原因是,我不能對流程的輸出(即InputStream)做任何處理,直到流程完成,所以我需要將它放入緩衝區中以供稍後閱讀。 – Rob 2009-06-25 19:19:03

+2

因此把它放入ByteArrayOutputStream的字節數組中。一旦你把它作爲一個字節數組,你就沒事。無論如何,這正是NIO所要做的,而BAOS則更簡單。如果它會很大,你可能會想要派生你自己的ByteArrayOutputStream,它可以讓你直接訪問字節數組,所以你不用擔心用toByteArray()創建一個副本。這是一個恥辱ByteArrayOutputStream沒有「toByteArrayInputStream」,讓你直接從它讀取... – 2009-06-25 19:24:57

4

您可以使用NIO,但這裏沒有真正的需要。作爲喬恩斯基特建議:

public byte[] read(InputStream istream) 
{ 
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    byte[] buffer = new byte[1024]; // Experiment with this value 
    int bytesRead; 

    while ((bytesRead = istream.read(buffer)) != -1) 
    { 
    baos.write(buffer, 0, bytesRead); 
    } 

    return baos.toByteArray(); 
} 


// after the process is run, we call this method with the String 
public void readLines(byte[] data) 
{ 
    BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data))); 
    String line; 

    while ((line = reader.readLine()) != null) 
    { 
    // do stuff with line 
    } 
} 
0

這是一個示例:

public class ByteBufferBackedInputStream extends InputStream { 

    ByteBuffer buf; 

    public ByteBufferBackedInputStream(ByteBuffer buf) { 
     this.buf = buf; 
    } 

    public synchronized int read() throws IOException { 
     if (!buf.hasRemaining()) { 
      return -1; 
     } 
     return buf.get() & 0xFF; 
    } 

    @Override 
    public int available() throws IOException { 
     return buf.remaining(); 
    } 

    public synchronized int read(byte[] bytes, int off, int len) throws IOException { 
     if (!buf.hasRemaining()) { 
      return -1; 
     } 

     len = Math.min(len, buf.remaining()); 
     buf.get(bytes, off, len); 
     return len; 
    } 
} 

而且你可以使用它像這樣:

String text = "this is text"; // It can be Unicode text 
    ByteBuffer buffer = ByteBuffer.wrap(text.getBytes("UTF-8")); 

    InputStream is = new ByteBufferBackedInputStream(buffer); 
    InputStreamReader r = new InputStreamReader(is, "UTF-8"); 
    BufferedReader br = new BufferedReader(r); 
    BufferedReader br = new BufferedReader(r);