【问题标题】:Is there any way to get the size in bytes of a string in Java?有没有办法在Java中获取字符串的字节大小?
【发布时间】:2012-11-23 17:25:11
【问题描述】:

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

【问题讨论】:

  • 您不需要字节,因为该文件仅包含字符(或者至少,您在问题中没有另外说明)。只需使用 line.length()。
  • 不,他要计算百分比。他首先以字节为单位读取 file.length(),然后他想对字符串字节求和以计算当前百分比。乍一看似乎很容易,但实际上并非如此,因为编码。
  • 啊,是的,咳咳,我明白了。 (对不起,伙计们,昏暗的时刻)

标签: java android


【解决方案1】:

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

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

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

请注意,所有这些都在某种程度上是虚构的……您已经读取了字节,将它们解码为文本,然后将它们重新编码为字节……

【讨论】:

    【解决方案2】:
    final String hello_str = "Hello World";
    
    hello_str.getBytes().length is the "byte size", i.e. the number of bytes
    

    【讨论】:

      【解决方案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 的#1

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

      • 例如,UTF-16 文本文件通常以特殊的 2 字节 BOM(字节顺序标记)开头,以表明它们是否必须解释小端或大端。当您仅查看从读者那里获得的 String 时,不会报告这 2 个(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% 或
        • 对于较大的文件,上述方法。文件越大越好。以 0.001% 的步长更新进度只会减慢速度。减小读取器的缓冲区大小会提高准确性,但也会降低读取性能。
      • 如果您有足够的时间:编写您自己的阅读器,告诉您确切的字节位置。可能是InputStreamReaderBufferedReader 的组合,因为Reader 已经对字符进行了操作。 Android'simplementation 可以作为起点提供帮助。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          假设您有一个名为 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"
          

          【讨论】:

            猜你喜欢
            • 2014-04-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-09-22
            • 1970-01-01
            相关资源
            最近更新 更多