Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 读取字符行并获取文件位置_Java_Nio_Java Io - Fatal编程技术网

Java 读取字符行并获取文件位置

Java 读取字符行并获取文件位置,java,nio,java-io,Java,Nio,Java Io,我正在从文本文件中读取连续的字符行。文件中字符的编码可能不是单字节 在某些情况下,我希望获得下一行开始的文件位置,以便稍后重新打开文件并快速返回到该位置 问题 有没有一种简单的方法可以同时做到这两个方面,最好使用标准Java库 如果不是,什么是合理的解决办法 理想解的属性 理想的解决方案是处理多字符编码。这包括UTF-8,其中不同的字符可以用不同的字节数表示。理想的解决方案主要依赖于一个受信任的、支持良好的库。最理想的是标准Java库。其次是Apache或Google库。解决方案必须是可伸缩的。

我正在从文本文件中读取连续的字符行。文件中字符的编码可能不是单字节

在某些情况下,我希望获得下一行开始的文件位置,以便稍后重新打开文件并快速返回到该位置

问题 有没有一种简单的方法可以同时做到这两个方面,最好使用标准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

  • 使用或在循环中使用
  • 检查下线字符,然后处理该行
  • 其他的问题是,你必须绝对确保你永远不会读过EOL字符

    readChar()返回字符而不是字节。因此,您不必担心字符宽度

    从该文件中读取一个字符。此方法从文件中读取两个字节,从当前文件指针开始

    [……]

    此方法会一直阻塞,直到读取了两个字节、检测到流结束或引发异常为止

    通过使用RandomAccessFile而不是Reader,您放弃了Java为您解码文件中字符集的能力。BufferedReader将自动执行此操作

    有几种方法可以克服这个问题。一种是自己检测编码,然后使用正确的read*()方法。另一种方法是使用BoundedInput流

    这个问题有一个

    例如,

    具有以下功能: 搜索(长位置)
    设置文件指针偏移量,从文件开头开始测量,在该偏移量处进行下一次读取或写入。

    此部分解决方法仅处理使用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
    }