【问题标题】:How to know bytes read(offset) of BufferedReader?如何知道 BufferedReader 的字节读取(偏移)?
【发布时间】:2013-02-26 15:25:57
【问题描述】:

我想逐行读取文件。 BufferedReader 比 RandomAccessFile 或 BufferedInputStream 快得多。 但问题是我不知道我读了多少字节。 如何知道读取的字节数(偏移量)? 我试过了。

String buffer;
int offset = 0;

while ((buffer = br.readLine()) != null)
    offset += buffer.getBytes().length + 1; // 1 is for line separator

如果文件很小,我会工作。 但是,当文件变大时,偏移量会小于实际值。 我怎样才能得到抵消?

【问题讨论】:

  • 你想要完成什么更大的任务?由于内部缓冲区(和编码,以及不同的行尾),这从根本上来说很棘手。
  • 我想获取行首的偏移量。因此,我稍后使用 RandomAccessFile 使用该偏移量来读取文件的某些部分。
  • 您假设只有一个行分隔符字节,例如\n。你能假设吗?
  • 其实我用的是line.separator而不是1。

标签: java io


【解决方案1】:

BufferedReader 没有简单的方法可以做到这一点,因为有两个影响:字符结尾编码和行结尾。在 Windows 上,行尾是 \r\n,即两个字节。在 Unix 上,行分隔符是一个字节。 BufferedReader 会在你不注意的情况下处理这两种情况,所以在readLine() 之后,你将不知道跳过了多少字节。

另外buffer.getBytes()只有在你的默认编码和文件中数据的编码偶然碰巧相同时才会返回正确的结果。当使用任何类型的byte[] String 转换时,您应该始终准确指定应该使用哪种编码。

您也不能使用计数InputStream,因为缓冲的读取器读取大块数据。因此,在读取第一行(例如 5 个字节)后,内部 InputStream 中的计数器将返回 4096,因为读取器总是将这么多字节读入其内部缓冲区。

你可以看看 NIO。您可以使用低级别 ByteBuffer 来跟踪偏移量并将其包装在 CharBuffer 中以将输入转换为行。

【讨论】:

  • 使用 BufferedReader 没有简单的方法可以做到这一点,因为它同时进行缓冲和新行检测。顺便说一句,感谢有关 ByteBuffer 和 CharBuffer 的提示
【解决方案2】:

这是应该起作用的东西。它假定为 UTF-8,但您可以轻松更改它。

import java.io.*;

class main {
    public static void main(final String[] args) throws Exception {
        ByteCountingLineReader r = new ByteCountingLineReader(new ByteArrayInputStream(toUtf8("Hello\r\nWorld\n")));

        String line = null;
        do {
            long count = r.byteCount();
            line = r.readLine();
            System.out.println("Line at byte " + count + ": " + line);
        } while (line != null);

        r.close();
    }

    static class ByteCountingLineReader implements Closeable {
        InputStream in;
        long _byteCount;
        int bufferedByte = -1;
        boolean ended;

        // in should be a buffered stream!
        ByteCountingLineReader(InputStream in) {
            this.in = in;
        }

        ByteCountingLineReader(File f) throws IOException {
            in = new BufferedInputStream(new FileInputStream(f), 65536);
        }

        String readLine() throws IOException {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            if (ended) return null;
            while (true) {
                int c = read();
                if (ended && baos.size() == 0) return null;
                if (ended || c == '\n') break;
                if (c == '\r') {
                    c = read();
                    if (c != '\n' && !ended)
                        bufferedByte = c;
                    break;
                }
                baos.write(c);
            }
            return fromUtf8(baos.toByteArray());
        }

        int read() throws IOException {
            if (bufferedByte >= 0) {
                int b = bufferedByte;
                bufferedByte = -1;
                return b;
            }
            int c = in.read();
            if (c < 0) ended = true; else ++_byteCount;
            return c;
        }

        long byteCount() {
            return bufferedByte >= 0 ? _byteCount - 1 : _byteCount;
        }

        public void close() throws IOException {
            if (in != null) try {
                in.close();
            } finally {
                in = null;
            }
        }

        boolean ended() {
            return ended;
        }
    }

    static byte[] toUtf8(String s) {
        try {
            return s.getBytes("UTF-8");
        } catch (Exception __e) {
            throw rethrow(__e);
        }
    }

    static String fromUtf8(byte[] bytes) {
        try {
            return new String(bytes, "UTF-8");
        } catch (Exception __e) {
            throw rethrow(__e);
        }
    }

    static RuntimeException rethrow(Throwable t) {

        throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
    }
}

【讨论】:

    【解决方案3】:

    尝试使用RandomAccessFile

         RandomAccessFile raf = new RandomAccessFile(filePath, "r");
         while ((cur_line = raf.readLine()) != null){
            System.out.println(curr_line);
            // get offset
            long rowIndex = raf.getFilePointer();
         }
    

    通过偏移寻找:

    raf.seek(offset);

    【讨论】:

      【解决方案4】:

      我想知道您的最终解决方案,但是,我认为使用 long 类型而不是 int 可以满足上述代码中的大多数情况。

      【讨论】:

        【解决方案5】:

        如果你想逐行读取文件,我会推荐这个代码:

        import java.io.*;
        class FileRead 
        {
         public static void main(String args[])
          {
          try{
          // Open the file that is the first 
          // command line parameter
          FileInputStream fstream = new FileInputStream("textfile.txt");
          // Use DataInputStream to read binary NOT text.
          BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
          String strLine;
          //Read File Line By Line
          while ((strLine = br.readLine()) != null)   {
          // Print the content on the console
          System.out.println (strLine);
          }
          //Close the input stream
          in.close();
            }catch (Exception e){//Catch exception if any
          System.err.println("Error: " + e.getMessage());
          }
          }
        }
        

        我过去一直使用这种方法,效果很好!

        来源:Here

        【讨论】:

        • 你的反应有点不对,因为你应该在 finally 块中关闭外部资源,你也没有回答这个问题,除此之外他正在使用类似的东西,但代码更紧凑例子。
        • 如果它来自印度玫瑰,你应该假设它只是大部分是正确的。你最好阅读任何其他网站。
        猜你喜欢
        • 2013-01-03
        • 1970-01-01
        • 2019-05-27
        • 1970-01-01
        • 2012-04-28
        • 1970-01-01
        • 1970-01-01
        • 2014-04-08
        • 2012-10-07
        相关资源
        最近更新 更多