有没有办法在Java中获取字符串的字节大小?

我需要文件中每行的大小(以字节为单位),因此我可以获得读取文件的百分比。 我已经使用file.length()获得了文件的大小,但是如何获得每行的大小?

您可能使用以下内容来读取文件

 FileInputStream fis = new FileInputStream(path); BufferedReader br = new BufferedReader(new InputStreamReader(fis, "UTF-8")); String line; while ((line = br.readLine()) != null) { /* process line */ /* report percentage */ } 

您需要在开头指定编码。 如果你不这样做,你应该在Android上获得UTF-8。 这是默认值,但可以更改。 我会假设没有设备可以做到这一点。

重复其他答案已经陈述的内容:字符数不总是与字节数相同。 特别是UTF编码很棘手。 目前有249,764个指定的Unicode字符,可能超过一百万( WP ),UTF使用1到4个字节来编码所有这些字符。 UTF-32是最简单的情况,因为它总是使用4个字节。 UTF-8动态地执行此操作并使用1到4个字节。 简单的ASCII字符仅使用1个字节。 (来源: UTF和BOM常见问题 )

获取可以使用的字节数,例如line.getBytes("UTF-8").length() 。 一个很大的缺点是,这是非常低效的,因为它每次都会创建String内部数组的副本,然后抛弃它。 这是在Android上解决的问题#1 性能提示

由于以下原因,从文件读取的实际字节数也不是100%准确:

  • 例如,UTF-16文本文件通常以特殊的2字节BOM(字节顺序标记)开始,以表示它们是否必须解释为小端或大端。 当你只看你从你的读者得到的String时,不报告那些2(UTF-8:3,UTF-32:4)字节。 所以你已经离开了一些字节。

  • 将文件的每一行转换为UTF-16 String将包括每行的BOM字节。 所以getBytes会为每一行报告2个字节太多。

  • 行结束字符不是结果line- String 。 更糟糕的是,你有不同的方式来表示一条线的结束。 通常是Unix-Style '\n' ,它只有1个字符,或者是Windows-Style '\r''\n' ,它是两个字符。 BufferedReader将简单地跳过这些。 在这里,您的计算缺少非常多的字节数。 从Unix / UTF-8的1个字节到Windows / UTF-32的8个字节。

如果你有Unix / UTF-16,最后两个原因会相互否定,但这可能不是典型的情况。 错误的影响还取决于行长度:如果每行总共只有4个字节的错误,那么总共只有10个字节,你的进度将非常错误(如果我的数学很好,你的进度将达到140%或者在最后一行之后的60%,取决于你的计算是假设每行-4或+4字节)

这意味着到目前为止,无论你做什么,你只能得到近似值。

如果您编写自己的特殊字节计数Reader ,可能会获得实际的字节数,但这将是相当多的工作。

另一种方法是使用自定义InputStream来计算从底层流中实际读取的字节数。 这不是太难做,也不关心编码。

最大的缺点是它不会随着你读取的行线性增加,因为BufferedReader将填充它的内部缓冲区并从那里读取行,然后从文件中读取下一个块,依此类推。 如果缓冲区足够大,则您已经在第一行处于100%。 但我认为你的文件足够大,或者你不想知道进展情况。

例如,这将是一种实现。 它有效,但我无法保证它是完美的。 如果stream使用mark()reset() ,它将无法工作。 文件阅读不应该这样做。

 static class CountingInputStream extends FilterInputStream { private long bytesRead; protected CountingInputStream(InputStream in) { super(in); } @Override public int read() throws IOException { int result = super.read(); if (result != -1) bytesRead += 1; return result; } @Override public int read(byte[] b) throws IOException { int result = super.read(b); if (result != -1) bytesRead += result; return result; } @Override public int read(byte[] b, int off, int len) throws IOException { int result = super.read(b, off, len); if (result != -1) bytesRead += result; return result; } @Override public long skip(long n) throws IOException { long result = super.skip(n); if (result != -1) bytesRead += result; return result; } public long getBytesRead() { return bytesRead; } } 

使用以下代码

 File file = new File("mytestfile.txt"); int linesRead = 0; long progress = 0; long fileLength = file.length(); String line; CountingInputStream cis = new CountingInputStream(new FileInputStream(file)); BufferedReader br = new BufferedReader(new InputStreamReader(cis, "UTF-8"), 8192); while ((line = br.readLine()) != null) { long newProgress = cis.getBytesRead(); if (progress != newProgress) { progress = newProgress; int percent = (int) ((progress * 100) / fileLength); System.out.println(String.format("At line: %4d, bytes: %6d = %3d%%", linesRead, progress, percent)); } linesRead++; } System.out.println("Total lines: " + linesRead); System.out.println("Total bytes: " + fileLength); br.close(); 

我输出像

 At line: 0, bytes: 8192 = 5% At line: 82, bytes: 16384 = 10% At line: 178, bytes: 24576 = 15% .... At line: 1621, bytes: 155648 = 97% At line: 1687, bytes: 159805 = 100% Total lines: 1756 Total bytes: 159805 

或者在相同文件的情况下UTF-16编码

 At line: 0, bytes: 24576 = 7% At line: 82, bytes: 40960 = 12% At line: 178, bytes: 57344 = 17% ..... At line: 1529, bytes: 303104 = 94% At line: 1621, bytes: 319488 = 99% At line: 1687, bytes: 319612 = 100% Total lines: 1756 Total bytes: 319612 

而不是打印,你可以更新你的进度。

那么,最好的方法是什么?

  • 如果您知道在编码中只有1个字节用于这些字符的简单ASCII文本:只需使用String#length() (并且可能为行结尾添加+1或+2) String#length()快速且简单,只要你知道你有什么文件,你应该没有问题。
  • 如果您的国际文本中的简单方法不起作用:
    • 对于较小的文件,处理每一行需要相当长的时间: String#getBytes() ,处理1行的时间越长,临时数组及其垃圾收集的影响越小。 不准确应在可接受的范围内。 如果进度> 100%或<100%,请确保不要崩溃。
    • 对于上述方法的较大文件。 文件越大越好。 以0.001%的步骤更新进度只会减慢速度。 减小读取器的缓冲区大小会提高精度,但也会降低读取性能。
  • 如果你有足够的时间:编写自己的Reader,告诉你确切的字节位置。 也许是InputStreamReaderBufferedReader的组合,因为Reader已经对字符进行了操作。 Android的 实施可能有助于作为起点。

你需要知道编码 – 否则这是一个毫无意义的问题。 例如,“foo”是UTF-16中的6个字节,但是ASCII中是3个字节。 假设你一次读一行(给出你的问题)你应该知道你正在使用哪种编码,因为你应该在开始阅读时指定它。

您可以调用String.getBytes(charset)来获取特定字符串的编码表示。

不要只调用String.getBytes() ,因为它将使用平台默认编码。

请注意,所有这些都是有点工作…你已经读取了字节,将它们解码为文本,然后你将它们重新编码为字节……

 final String hello_str = "Hello World"; hello_str.getBytes().length is the "byte size", ie the number of bytes 

如果File是ASCII文件,那么你可以使用String.length(); 另外,它变得更加复杂。

假设您有一个名为hello_str的字符串变量

 final String hello_str = "Hello World"; //Check Character length hello_str.length() //output will be 11 // Check encoded sizes final byte[] utf8Bytes = hello_str.getBytes("UTF-8"); utf8Bytes.length //output will be 11 final byte[] utf16Bytes= hello_str.getBytes("UTF-16"); utf16Bytes.length // output will be "24" final byte[] utf32Bytes = hello_str.getBytes("UTF-32"); utf32Bytes.length // output will be "44"