我需要阅读和处理一个巨大的文本文件。为了提高数据处理时间,我想到了让多个阅读器同时阅读。这个想法是通过记下开始和结束指针来虚拟分割文件。这是由程序开始时的主线程完成的。实际上我的意思是,不创建物理拆分文件。
稍后当并发读取器完成读取和处理时,每个线程可以调用 bufferedReader.skip(long) 并跟踪读取的字符数,以便它们不会越过结束指针边界。
问题是单个线程完成的文件读取是使用 BufferedReader 完成的,因此要跳过我需要知道字符数,而主线程无法确定这一点。要计算开始和结束指针,主线程唯一的数据是以字节为单位的文件长度。
如何根据字符确定开始和结束指针,以便读者可以跳过那么多字符?
笔记 -
- 输入文本文件可以采用不同的字符编码,例如 ASCII、EBCDIC、UTF-8、UTF-16 等。
- 逐行读取输入文件以确定开始和结束指针不是一种选择,因为它违背了分割文本文件的目的。
更新
注意我只能使用 java 文件 API 而不是像 Hadoop 这样的框架。这是应用程序架构限制
更新
这是通过跳过计算的字节数然后逐字节读取输入文件以确定记录分隔符来读取输入文件的代码。如果您发现代码有问题,请回复您的想法(特别是考虑到输入文件可能采用不同的字符编码这一事实)。
{
CountingInputStream countingInputStream = new CountingInputStream(new FileInputStream(inputFilePath.toFile()));
long endPointer;
while(true) {
long actualSkipped = countingInputStream.skip(skipCount);
if(actualSkipped == 0) {
logger.info("Nothing to skip");
break; //nothing to skip now.
}
byte[] inputBytes = new byte[recordDelimiterBytes.length];
int noOfBytesRead = countingInputStream.read(inputBytes);
if(noOfBytesRead == -1) {
//end of file already reached!
endPointer = countingInputStream.getCount();
break;
}
while (!(Arrays.equals(recordDelimiterBytes, inputBytes))) {
shiftLeft(inputBytes);
int readByte = countingInputStream.read();
if(readByte != -1) {
inputBytes[inputBytes.length - 1] = (byte) readByte;
} else {
throw new IllegalStateException("EOF reached before getting the delimiter");
}
}
endPointer = countingInputStream.getCount();
}
private void shiftLeft(byte[] inputBytes) {
for(int i=0; i<inputBytes.length - 1; i++) {
inputBytes[i] = inputBytes[i+1];
}
}