有没有办法在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,告诉你确切的字节位置。 也许是
InputStreamReader
和BufferedReader
的组合,因为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"