Java 读取字符行并获取文件位置
我正在从文本文件中读取连续的字符行。文件中字符的编码可能不是单字节 在某些情况下,我希望获得下一行开始的文件位置,以便稍后重新打开文件并快速返回到该位置 问题 有没有一种简单的方法可以同时做到这两个方面,最好使用标准Java库 如果不是,什么是合理的解决办法 理想解的属性 理想的解决方案是处理多字符编码。这包括UTF-8,其中不同的字符可以用不同的字节数表示。理想的解决方案主要依赖于一个受信任的、支持良好的库。最理想的是标准Java库。其次是Apache或Google库。解决方案必须是可伸缩的。将整个文件读入内存不是一个解决方案。返回到一个位置不需要在线性时间内读取所有先前的字符 细节 对于第一个需求,Java 读取字符行并获取文件位置,java,nio,java-io,Java,Nio,Java Io,我正在从文本文件中读取连续的字符行。文件中字符的编码可能不是单字节 在某些情况下,我希望获得下一行开始的文件位置,以便稍后重新打开文件并快速返回到该位置 问题 有没有一种简单的方法可以同时做到这两个方面,最好使用标准Java库 如果不是,什么是合理的解决办法 理想解的属性 理想的解决方案是处理多字符编码。这包括UTF-8,其中不同的字符可以用不同的字节数表示。理想的解决方案主要依赖于一个受信任的、支持良好的库。最理想的是标准Java库。其次是Apache或Google库。解决方案必须是可伸缩的。
BufferedReader.readLine()
很有吸引力。但缓冲显然会干扰获得有意义的文件位置
不太明显的是,InputStreamReader
也可以提前读取,从而干扰获取文件位置。从:
为了实现字节到字符的高效转换,可以从底层流中提前读取比满足当前读取操作所需的更多字节
方法RandomAccessFile.readLine()
每个字节都转换为一个字符,方法是取字符低八位的字节值,并将字符高八位设置为零。因此,此方法不支持完整的Unicode字符集
如果您从
文件读取器
构造缓冲读取器
,并保持代码可以访问文件读取器
的实例,您应该能够通过调用以下命令获得下一行的位置:
fileReader.getChannel().position();
调用bufferedReader.readLine()
后
如果您愿意用性能增益换取位置精度,则可以使用大小为1的输入缓冲区构建BufferedReader
替代解决方案
自己跟踪字节会有什么问题:
long startingPoint = 0; // or starting position if this file has been previously processed
while (readingLines) {
String line = bufferedReader.readLine();
startingPoint += line.getBytes().length;
}
这将使字节计数精确到您已经处理的内容,而不管底层标记或缓冲。您必须在理货单中说明行尾,因为行尾已被剥离。我建议
java.io.LineNumberReader
。您可以设置并获取行号,从而在特定的行索引处继续
由于它是一个
缓冲读取器
,因此它还能够处理UTF-8。解决方案a
设置文件指针偏移量,从文件开头开始测量,在该偏移量处进行下一次读取或写入。此部分解决方法仅处理使用7位ASCII或UTF-8编码的文件。一个带有一般解决方案的答案仍然是可取的(对这种变通方法的批评也是如此) 在UTF-8中:
- 所有单字节字符都可以与多字节字符中的所有字节区分开来。多字节字符中的所有字节在高位都有一个“1”。特别是,表示LF和CR的字节不能是多字节字符的一部分李>
- 所有单字节字符均采用7位ASCII格式。因此,我们可以使用UTF-8解码器解码仅包含7位ASCII字符的文件
RandomAccessFile
。该类提供了读取行和获取/设置文件位置的方法
下面是使用RandomAccessFile将下一行读取为UTF-8的代码草图
protected static String
readNextLineAsUTF8( RandomAccessFile in ) throws IOException {
String rv = null;
String lineBytes = in.readLine();
if ( null != lineBytes ) {
rv = new String( lineBytes.getBytes(),
StandardCharsets.UTF_8 );
}
return rv;
}
然后,可以在调用该方法之前立即从RandomAccessFile中获取文件位置。给定由中的引用的随机访问文件:
long startPos = in.getFilePointer();
String line = readNextLineAsUTF8( in );
VTD-XML似乎解决了这个问题,它是一个能够快速解析大型XML文件的库:
上一个java VTD-XML ximpleware实现(目前为2.13)提供了一些代码,在每次调用其IReader实现的getChar()方法后,都会维护字节偏移量
VTDGen.java和VTDGenHuge.java中提供了各种字符编码的IReader实现
IReader实现是为以下编码提供的
ASCII码;
ISO_8859_1
ISO_8859_10
ISO_8859_11
ISO_8859_12
ISO_8859_13
ISO_8859_14
ISO_8859_15
ISO_8859_16
ISO_8859_2
ISO_8859_3
ISO_8859_4
ISO_8859_5
ISO_8859_6
ISO_8859_7
ISO_8859_8
ISO_8859_9
UTF_16BE
UTF_16LE
UTF8
温杜1250
温杜1251
温杜1252
温杜1253
温杜1254
温杜1255
温杜1256
温杜1257
WIN_1258最初,我发现建议的方法是b
fun RandomAccessFile.readEncodedLine(charset: Charset = Charsets.UTF_8): String? {
val lineBytes = ByteArrayOutputStream()
var c = -1
var eol = false
while (!eol) {
c = read()
when (c) {
-1, 10 -> eol = true // \n
13 -> { // \r
eol = true
val cur = filePointer
if (read() != '\n'.toInt()) {
seek(cur)
}
}
else -> lineBytes.write(c)
}
}
return if (c == -1 && lineBytes.size() == 0)
null
else
java.lang.String(lineBytes.toByteArray(), charset) as String
}