验证 Java 中的字符串是否为 UTF-8 编码

voi*_*oid 3 java string encoding utf-8 utf-16

有很多方法可以检查字符串是否是 UTF-8 编码的,例如:

public static boolean isUTF8(String s){
    try{
        byte[]bytes = s.getBytes("UTF-8");
    }catch(UnsupportedEncodingException e){
        e.printStackTrace();
        System.exit(-1);
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

的文档java.lang.String#getBytes(java.nio.charset.Charset)说:

此方法始终用此字符集的默认替换字节数组替换格式错误的输入和不可映射的字符序列。

  1. 它总是返回正确的 UTF-8 字节是否正确?
  2. String对对象执行此类检查是否有意义?true当 String 对象已经编码时,它不会总是返回吗?
  3. 据我了解,此类检查应该在字节上执行,而不是在String对象上执行:
public static final boolean isUTF8(final byte[] inputBytes) {
    final String converted = new String(inputBytes, StandardCharsets.UTF_8);
    final byte[] outputBytes = converted.getBytes(StandardCharsets.UTF_8);
    return Arrays.equals(inputBytes, outputBytes);
}
Run Code Online (Sandbox Code Playgroud)

但在这种情况下,我不确定我是否理解应该从哪里获取这些butes,因为直接从对象中获取它String是不正确的。

And*_*eas 5

它总是返回正确的 UTF-8 字节是否正确?

是的。

对 String 对象执行此类检查是否有意义?当 String 对象已经编码时,它不会总是返回 true 吗?

Java 字符串使用以 UTF-16 编码的 Unicode 字符。由于 UTF-16 使用代理项对,因此任何未配对的代理项都是无效的,因此 Java 字符串可能包含无效char序列。

Java 字符串还可以包含 Unicode 中未分配的字符。

这意味着在 Java 上执行验证String是有意义的,尽管很少这样做。

据我了解,此类检查应该对字节执行,而不是对 String 对象执行。

根据字节的字符集,没有什么可以验证的,例如字符集CP437映射所有256字节值,因此它不可能是无效的。

UTF-8 可能无效,因此您认为验证字节很有用是正确的。


正如 javadoc 所说,getBytes(Charset)始终用字符集的默认替换字节替换格式错误的输入和不可映射的字符序列。

那是因为它这样做:

CharsetEncoder encoder = charset.newEncoder()
        .onMalformedInput(CodingErrorAction.REPLACE)
        .onUnmappableCharacter(CodingErrorAction.REPLACE);
Run Code Online (Sandbox Code Playgroud)

如果您想获取字节,但在格式错误的输入和不可映射的字符序列上失败,请改用CodingErrorAction.REPORT。因为这实际上是默认的,所以不要调用这两个onXxx()方法。

例子

String s = "\uD800"; // unpaired surrogate
System.out.println(Arrays.toString(s.getBytes(StandardCharsets.UTF_8)));
Run Code Online (Sandbox Code Playgroud)

打印[63]which is a ?,即未配对的代理是格式错误的输入,因此它被替换为替换字节。

String s = "\uD800"; // unpaired surrogate

CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
ByteBuffer encoded = encoder.encode(CharBuffer.wrap(s.toCharArray()));
byte[] bytes = new byte[encoded.remaining()];
encoded.get(bytes);

System.out.println(Arrays.toString(bytes));
Run Code Online (Sandbox Code Playgroud)

这是MalformedInputException: Input length = 1因为默认的格式错误输入操作是REPORT