如何正确计算字符串的字节数?

6

一个包含特殊字符如ç在每个特殊字符上占用两个字节的大小,但是使用String length method或从getBytes method返回的字节数组获取其长度时,特殊字符并不会被计算为两个字节。

我该如何正确计算String中的字节数?

例子:

单词endereço应该返回长度9而不是8。


当我运行 System.out.println("endereço".getBytes().length); 时,它会打印出 "9"。 - briarheart
@briarheart getBytes() 使用平台默认编码,可能已经是 UTF-8。请参阅:不同平台上的平台默认字符集? - avojak
我正在使用Java 8。我认为“utf-8”是Java的任何版本的默认编码,除非明确覆盖此行为。 - briarheart
4
UTF-8并不是任何Java版本的默认编码格式。默认编码通常由操作系统定义,Linux上通常为UTF-8,但在Windows上很少为UTF-8。 - Andreas
1
长度取决于编码方式,例如对于 endereço,在ISO-8859-1编码下为 8,UTF-8编码下为 9,EUC-JP编码下为 10,UTF-16BE编码下为 16,UTF-32编码下为 32。 - Andreas
显示剩余7条评论
1个回答

15
单词“endereço”应该返回长度为9而不是8。
如果您期望具有8个字符长度的“endereço”字符串的大小为9字节:7个ASCII字符和1个非ASCII字符,则我假设您想使用UTF-8字符集,该字符集对于包含在ASCII表中的字符使用1个字节,并且对于其他字符使用更多字节。
但是,使用String.length()方法或使用从getBytes方法返回的字节数组获取其长度的方法不会将特殊字符视为两个字节进行计算。
String.length()方法并不能回答问题:“使用了多少字节?”但是回答了:“包含多少个“UTF-16代码单元”,或者更简单地说,有多少个字符?”
String.length() Javadoc:
返回此字符串的长度。长度等于字符串中的Unicode代码单元数。
getBytes()方法将String编码为一个字节数组。您可以使用返回数组的length属性来知道编码后使用了多少字节,但结果取决于编码过程中使用的字符集。但是,getBytes()方法不允许指定字符集:它使用平台的默认字符集。因此,如果底层操作系统默认使用的字符集与您要用于将字符串编码为字节的字符集不同,则使用它可能无法获得预期的结果。此外,根据应用程序部署的平台,字符串以字节形式编码的方式可能会发生变化。这可能是不希望看到的。最后,如果字符串无法在默认字符集中编码,则行为是未指定的。因此,应非常谨慎地使用此方法或根本不使用它。
getBytes() Javadoc:
使用平台的默认字符集将此字符串编码为字节序列,并将结果存储到新的字节数组中。当该字符串无法用默认字符集进行编码时,此方法的行为是未指定的。当需要更多对编码过程进行控制时,应使用java.nio.charset.CharsetEncoder类。
在您的字符串示例"endereço"中,如果getBytes()返回大小为8而不是9的数组,则意味着您的操作系统不使用默认的UTF-8字符集,而是使用1个字节固定宽度的字符集,例如ISO 8859-1及其派生字符集,如基于Windows操作系统的windows-1252
要知道应用程序运行的当前Java虚拟机的默认字符集,可以使用此实用程序方法:Charset defaultCharset = Charset.defaultCharset()
解决方案 byte[] getBytes()方法带有另外两个非常有用的重载:
- byte[] java.lang.String.getBytes(String charsetName) throws UnsupportedEncodingException - byte[] java.lang.String.getBytes(Charset charset) 与没有参数的getBytes()方法相反,这些方法允许指定在字节编码期间要使用的字符集。 byte[] java.lang.String.getBytes(String charsetName) throws UnsupportedEncodingException Javadoc:
将此字符串使用命名字符集编码为字节序列,并将结果存储到新的字节数组中。 当无法将此字符串编码为给定字符集时,此方法的行为是未指定的。当需要更多控制编码过程时,应使用java.nio.charset.CharsetEncoder类。 byte[] java.lang.String.getBytes(Charset charset) Javadoc:
将此字符串使用给定字符集编码为字节序列,并将结果存储到新的字节数组中。 此方法始终使用此字符集的默认替换字节数组替换格式不正确的输入和无法映射的字符序列。当需要更多控制编码过程时,应使用java.nio.charset.CharsetEncoder类。
您可以使用其中一个(虽然它们之间有一些复杂性)来将您的字符串编码为字节数组,以使用UTF-8或任何其他字符集并获取其特定字符集的大小。
例如,要通过使用getBytes(String charsetName)获取UTF-8编码的字节数组,可以这样做:
String yourString = "endereço";
byte[] bytes = yourString.getBytes("UTF-8");
int sizeInBytes = bytes.length;

你将得到一个9字节的长度,与你所希望的一样。

以下是更全面的示例,包括默认编码显示,使用默认字符集平台的字节编码,UTF-8UTF-16

public static void main(String[] args) throws UnsupportedEncodingException {

    // default charset
    Charset defaultCharset = Charset.defaultCharset();
    System.out.println("default charset = " + defaultCharset);

    // String sample
    String yourString = "endereço";

    //  getBytes() with default platform encoding
    System.out.println("getBytes() with default charset, size = " + yourString.getBytes().length + System.lineSeparator());

    // getBytes() with specific charset UTF-8
    System.out.println("getBytes(\"UTF-8\"), size = " + yourString.getBytes("UTF-8").length);       
    System.out.println("getBytes(StandardCharsets.UTF_8), size = " + yourString.getBytes(StandardCharsets.UTF_8).length + System.lineSeparator());

    // getBytes() with specific charset UTF-16      
    System.out.println("getBytes(\"UTF-16\"), size = " + yourString.getBytes("UTF-16").length);     
    System.out.println("getBytes(StandardCharsets.UTF_16), size = " + yourString.getBytes(StandardCharsets.UTF_16).length);
}

我在使用Windows操作系统的电脑上看到的输出:

默认字符集 = windows-1252

使用默认字符集的getBytes(),大小为8

getBytes("UTF-8"),大小为9

getBytes(StandardCharsets.UTF_8),大小为9

getBytes("UTF-16"),大小为18

getBytes(StandardCharsets.UTF_16),大小为18


字符串的length()方法并不能回答这个问题:使用了多少字节?但是它可以回答这个问题:"包含了多少个字符?" 它返回的是字符串中UTF-16代码单元的数量。每个代码点可能有多个代码单元,每个“字形簇”(大多数用户认为是一个字符)可能有多个代码点。 - plugwash
@plugwash 从技术角度来说,你是对的。我觉得我用词太粗俗了。我应该更具体地说:“包含多少个char?”我已经更新了。感谢您提出这个相关的评论 :) - davidxxx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接