我正在用Java维护一个后端服务,我使用Java 8代码的以下方法来验证服务API的输入:
private static boolean containsDisallowedChars(String toValidate) {
return !StandardCharsets.US_ASCII.newEncoder().canEncode(toValidate);
}
我正在扩展它以支持印地语和其他非英语字符,因此我将它从ASCII改为UTF-8,如下所示:
private static boolean containsDisallowedChars(String toValidate) {
return !StandardCharsets.UTF_8.newEncoder().canEncode(toValidate);
}
现在,我尝试更新相应的单元测试,以传入一个String toValidate,这将导致此方法返回false。
如何创建包含无法编码为UTF-8的内容的Java字符串?
我试过这个测试装置
// ref https://stackoverflow.com/questions/1301402/example-invalid-utf8-string
// test data byte values https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
// 3.5 Impossible bytes
// The following two bytes cannot appear in a correct UTF-8 string
// 3.5.1 fe = "�"
// 3.5.2 ff = "�"
// 3.5.3 fe fe ff ff = "����"
final byte[] bytes = {(byte)0xfe, (byte)0xfe, (byte)0xff, (byte)0xff};
log.info("bytes={}", bytes);
final String s = new String(bytes);
log.info("s={}", s);
log.info("s.length={}", s.length());
log.info("s.bytes={}", s.getBytes());
StandardCharsets.UTF_8.newEncoder().canEncode(s)返回true,日志输出显示String类构造函数正在更改字节数组,如下所示:
bytes=[-2, -2, -1, -1]
s=����
s.length=4
s.bytes=[-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67]
我使用https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt中描述的其他无效UTF-8字节数组尝试了几种变体,得到了类似的结果
尽管我努力提供了无效的字节数组,但String类似乎仍能健壮地创建有效的UTF-8字符串。
我按照这里的建议尝试了Base64 How can I generate non-UTF-8 string / char in Java for testing purposes?
final byte[] bytes = {(byte)0xfe, (byte)0xfe, (byte)0xff, (byte)0xff};
log.info("bytes={}", bytes);
final String s = new String(Base64.getEncoder().encode(bytes));
log.info("s={}", s);
log.info("s.length={}", s.length());
log.info("s.bytes={}", s.getBytes());
Base64.getEncoder().encode不返回字符串,它返回byte[]。因此,我仍然必须调用new String(byte[]),它将字节数组更改为有效的UTF-8字节数组。StandardCharsets.UTF_8.newEncoder().canEncode仍然返回true,我得到以下日志输出:
bytes=[-2, -2, -1, -1]
s=/v7//w==
s.length=8
s.bytes=[47, 118, 55, 47, 47, 119, 61, 61]
是否可以创建一个Java String对象,其中包含一个不能编码为UTF-8的字符串?如果不可以,是否意味着我的containsDisallowedChars
方法是不必要的,因为它永远不会返回true?或者我应该考虑一种不同的验证方法来代替StandardCharsets.UTF_8.newEncoder().canEncode?
1条答案
按热度按时间nlejzf6q1#
你在问题中指出:
尽管我努力提供了无效的字节数组,但String类似乎仍能健壮地创建有效的UTF-8字符串。
如果要测试字节数组以查看它对于特定编码是否有效,则可以使用
CharsetDecoder
(而不是CharsetEncoder
)。CharsetDecoder
可以:将特定字符集中的字节序列转换为十六位Unicode字符序列。
如果将
decode()
方法传递给ByteBuffer
,则可以按如下方式使用它:例如,下面的代码将打印
false
,因为0xFF
不是一个有效的UTF-8字节序列。示例
{(byte)0xfe, (byte)0xfe, (byte)0xff, (byte)0xff}
也将返回false
。在你的问题中,你问:
是否可以创建一个包含不能编码为UTF-8的字符串的Java String对象?
当您创建了一个JavaString时,要进行检查已经"太晚"了,因为,正如您所看到的,任何不受支持的字节序列都已经被Unicode replacement character所取代--Unicode replacement character本身是Java字符串中的有效字符(Java
String
对象本身"表示UTF-16格式的字符串"--UTF-8和UTF-16都覆盖了所有有效的Unicode代码点)。