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

时间:2012-11-23 17:25:12

标签: java android

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

5 个答案:

答案 0 :(得分:6)

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

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

只需拨打String.getBytes(),因为它将使用平台默认编码。

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

答案 1 :(得分:3)

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

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 FAQ

获取可以使用的字节数,例如line.getBytes("UTF-8").length()。一个很大的缺点是效率非常低,因为它每次都会创建String内部数组的副本,然后抛出它。这是在Android | Performance Tips

处理的第一个问题

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

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

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

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

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

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

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

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

最大的缺点是它不会与你读取的行线性增加,因为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

或者在同一文件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

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

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

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

答案 2 :(得分:3)

final String hello_str = "Hello World";

hello_str.getBytes().length is the "byte size", i.e. the number of bytes

答案 3 :(得分:0)

如果File是ASCII文件,那么你可以使用String.length(); 否则会变得更复杂。

答案 4 :(得分:0)

考虑一下名为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"