故事
我最近有問題...
我必須逐個字符地反向讀取檔案,而不會耗盡記憶體。
我無法逐行讀取并反轉它,StringBuilder因為它是一個占用高達千兆 (GB) 的 I/O 空間的單行檔案。
因此它會占用太多 JVM(和系統)的記憶體。
我決定從頭到尾(從頭到尾)逐個字符地閱讀它,這樣我就可以在不消耗太多記憶體的情況下盡可能多地處理它。
我試過的
我知道如何一次性讀取檔案:
( MappedByteBuffer FileChannel Charset這給了我OutOfMemoryExceptions)
并使用 UTF-8 字符支持逐字符讀取檔案
( FileInputStream InputStreamReader)。
問題是FileInputStream's #read()只呼叫#read0()這是本機方法!
因此,我不知道底層代碼......
這就是為什么我今天在這里(或至少在完成之前)!
uj5u.com熱心網友回復:
這會做到的(但正如所寫的那樣,它不是很有效)。
- 只需跳到最后一個位置,讀取少一個并讀取并列印字符。
- 然后將位置重置為標記,調整大小并繼續。
File f = new File("Some File name");
int size = (int) f.length();
int bsize = 1;
byte[] buf = new byte[bsize];
try (BufferedInputStream b =
new BufferedInputStream(new FileInputStream(f))) {
while (size > 0) {
b.mark(size);
b.skip(size - bsize);
int k = b.read(buf);
System.out.print((char) buf[0]);
size -= k;
b.reset();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
這可以通過增加緩沖區大小和對標記和跳過引數進行等效調整來改進。
更新后的版本
我對我的回答并不完全滿意,所以我把它變得更籠統。一些變數可以起到雙重作用,但使用有意義的名稱有助于闡明它們的使用方式。
Mark必須使用所以reset可以使用。但是,它只需要設定一次,并且在主回圈之外設定為位置 0。我不知道標記更接近讀取點是否更有效。skipCnt- 最初設定為fileLength讀取前要跳過的位元組數。如果剩余位元組數大于緩沖區大小,則跳過計數將為skipCnt - bsize. 否則它將是0。remainingBytes- 仍有多少位元組需要讀取。它通過減去當前的 來更新readCnt。readCnt- 要讀取多少位元組。如果remainingBytes大于bsize則設定為bsize,否則設定為remainingBytes
while 回圈從接近末尾的位置開始連續讀取檔案,然后以相反的順序列印剛剛讀取的資訊。更新所有變數并重復該程序,直到remainingBytes達到 0。
File f = new File("some file");
int bsize = 16;
int fileSize = (int)f.length();
int remainingBytes = fileSize;
int skipCnt = fileSize;
byte[] buf = new byte[bsize];
try (BufferedInputStream b =
new BufferedInputStream(new FileInputStream(f))) {
b.mark(0);
while(remainingBytes > 0) {
skipCnt = skipCnt > bsize ? skipCnt - bsize : 0;
b.skip(skipCnt);
int readCnt = remainingBytes > bsize ? bsize : remainingBytes;
b.read(buf,0,readCnt);
for (int i = readCnt-1; i >= 0; i--) {
System.out.print((char) buf[i]);
}
remainingBytes -= readCnt;
b.reset();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
uj5u.com熱心網友回復:
這不支持多位元組 UTF-8 字符
使用 aRandomAccessFile您可以輕松地從頭到尾以塊的形式讀取檔案,并反轉每個塊。
這是一個簡單的例子:
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.stream.IntStream;
class Test {
private static final int BUF_SIZE = 10;
private static final int FILE_LINE_COUNT = 105;
public static void main(String[] args) throws Exception {
// create a large file
try (FileWriter fw = new FileWriter("largeFile.txt")) {
IntStream.range(1, FILE_LINE_COUNT).mapToObj(Integer::toString).forEach(s -> {
try {
fw.write(s "\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
// reverse the file
try (RandomAccessFile raf = new RandomAccessFile("largeFile.txt", "r")) {
long size = raf.length();
byte[] buf = new byte[BUF_SIZE];
for (long i = size - BUF_SIZE; i > -BUF_SIZE; i -= BUF_SIZE) {
long offset = Math.max(0, i);
long readSize = Math.min(i BUF_SIZE, BUF_SIZE);
raf.seek(offset);
raf.read(buf, 0, (int) readSize);
for (int j = (int) readSize - 1; j >= 0; j--) {
System.out.print((char) buf[j]);
}
}
}
}
}
這使用了一個非常小的檔案和非常小的塊,以便您可以輕松地對其進行測驗。增加這些常數以查看它在更大范圍內的作業。
輸入檔案包含換行符,以便于讀取輸出,但反轉不依賴于檔案“有行”。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/497975.html
下一篇:洗掉文本檔案中后跟空格的換行符
