我需要获取文件中每行的字节数,以便计算文件读取的百分比。我已经使用file.length()
获得了文件的大小,但如何获取每行的大小?
final String hello_str = "Hello World";
hello_str.getBytes().length is the "byte size", i.e. the number of bytes
String.getBytes(charset)
来获取特定字符串的编码表示。String.getBytes()
,因为那会使用平台默认的编码方式。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 */
}
line.getBytes("UTF-8").length()
。一个很大的缺点是这非常低效,因为它每次都会创建字符串内部数组的副本,然后丢弃它。这就是Android | Performance Tips中提到的问题#1。例如,UTF-16文本文件通常以特殊的2字节BOM(字节顺序标记)开头,以指示它们是小端还是大端解释。当您查看从读取器获取的String
时,这2个(UTF-8:3,UTF-32:4)字节不会被报告。因此,在此处您已经少了一些字节。
将文件的每一行转换为UTF-16 String
将为每行包括这些BOM字节。因此,getBytes
每行将报告多2个字节。
行结束字符不是结果行String
的一部分。更糟糕的是,您有不同的方式来表示行的结束。通常是Unix风格的'\n'
,它只有1个字符,或者是Windows风格的'\r''\n'
,它是两个字符。 BufferedReader
将简单地跳过它们。在这里,您的计算缺少非常可变数量的字节。从Unix / UTF-8的1个字节到Windows / UTF-32的8个字节。
BufferedReader
会填充其内部缓冲区并从中读取行,然后从文件中读取下一个块,以此类推。如果缓冲区足够大,则您已经在第一行时就达到了100%。但我假设您的文件足够大,否则您就不会想了解进度。
例如,以下是这样的实现。它可以工作,但我不能保证它是完美的。如果流使用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
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
不要打印,你可以更新自己的进度。
那么,最好的方法是什么?
String#length()
(可能还要添加+1或+2以获取行尾)
String#length()
快速简单,只要您知道您拥有的文件,您就不应该有任何问题。String#getBytes()
,处理1行所需的时间越长,则临时数组及其垃圾收集的影响越小。不准确度应在可接受范围内。只需确保在结束时不会崩溃,如果进度> 100%或<100%。InputStreamReader
和BufferedReader
的组合,因为Reader已经在字符上操作。Android的 实现可以作为起点。假设您有一个名为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"