Java:如何读取文件的最后一个字符

Posted

技术标签:

【中文标题】Java:如何读取文件的最后一个字符【英文标题】:Java: How to read the last chars of a file 【发布时间】:2018-12-23 01:02:33 【问题描述】:

如何以最高的磁盘效率读取文件的最后几个字符?

【问题讨论】:

“有可能吗?” 是的! --- Why is “Is it possible to…” a poorly worded question? @Andreas:不,实际上这是不可能的。您只能按顺序读取数据,这与Java无关,而是操作系统限制 @HovercraftFullOfEels 当然有可能。问题是关于“以更高的磁盘效率这样做”,您可以通过随机访问读取最后一个块,处理这些字节(反向),然后读取second-last 块,进程字节,...最后读取第一个块,进程字节。更大的块大小将提高效率。如果您愿意,您甚至可以将字符设置为 Reader random-access,这就是我要搜索的内容,谢谢 @LucasNoetzold 如果您的文本文件使用单字节字符集(例如不是 UTF-8),那么“最后 4 个字符”表示“最后 4 个字节”,那么是的,使用 @987654323 @ 只读取最后 4 个字节可能是最快的方式。只有剖析才能确定。 【参考方案1】:

这是一种从 UTF-8 编码的文本文件中读取最后 N 个字符的方法。

/**
 * Reads last @code length characters from UTF-8 encoded text file.
 * <p>
 * The returned string may be shorter than requested if file is too
 * short, if the leading character is a half surrogate-pair, or if
 * file has invalid UTF-8 byte sequences.
 * 
 * @param fileName Name of text file to read.
 * @param length Length of string to return.
 * @return String with up to @code length characters.
 * @throws IOException if an I/O error occurs.
 */
public static String readLastChars(String fileName, int length) throws IOException 
    // A char can only store characters in the Basic Multilingual Plane, which are
    // encoded using up to 3 bytes each. A character from a Supplemental Plane is
    // encoded using 4 bytes, and is stored in Java as a surrogate pair, ie. 2 chars.
    // Worst case (assuming valid UTF-8) is that file ends with a 4-byte sequence
    // followed by length-1 3-byte sequences, so we need to read that many bytes.
    byte[] buf;
    try (RandomAccessFile file = new RandomAccessFile(fileName, "r")) 
        int bytesToRead = length * 3 + 1;
        buf = new byte[bytesToRead <= file.length() ? bytesToRead : (int) file.length()];
        file.seek(file.length() - buf.length);
        file.readFully(buf);
    
    // Scan bytes backwards past 'length' characters
    int start = buf.length;
    for (int i = 0; i < length && start > 0; i++) 
        if (buf[--start] < 0)  // not ASCII
            // Locate start of UTF-8 byte sequence (at most 4 bytes)
            int minStart = (start > 3 ? start - 3 : 0);
            while (start > minStart && (buf[start] & 0xC0) == 0x80)
                start--; // Skip UTF-8 continuation byte
            if (start == minStart)
                i++; // 4-byte UTF-8 -> 2 surrogate chars
        
    
    // Create string from bytes, and skip first character if too long
    // (text starts with surrogate pair, assuming valid UTF-8)
    String text = new String(buf, start, buf.length - start, StandardCharsets.UTF_8);
    while (text.length() > length)
        text = text.substring(text.offsetByCodePoints(0, 1));
    return text;

【讨论】:

我建议您将“字符”替换为“代码点”,除非您的意思是 lengthchar (UTF-16 代码单元)的数量——然后进入一般问题索引到 Java 的 UTF-16 字符串。此外,如果文件有 BOM,则不应将其包含在返回的文本中——因为它是元数据,而不是文本。否则,很好的答案。 @TomBlodget length 参数 char 的数量,即返回的String 的期望length(),这就是为什么javadoc 说“如果前导字符是半代理对”,则返回的字符串可能比请求的短 [...]”。例如。如果文件都是表情符号,并且您要求 9 个字符,它会返回 8 个代理对,即 4 个代码点,如设计。 --- Java 运行时库不支持 UTF-8 的 BOM,即所有 Java 方法都将 BOM 作为常规字符返回。 --- 这种方法没有理由偏离任何一个 Java 标准。

以上是关于Java:如何读取文件的最后一个字符的主要内容,如果未能解决你的问题,请参考以下文章

java如何高效读取文本的第N行和最后N行?

java读取UTF-8的txt文件发现开头的一个字符问题

在java中如何修改文本文件中的某一行的某些数据??

Java截取最后一个 _ 后面的所有字符

java如何读取InputStream中的字符串

java如何读取压缩包中的文本文件