我正在努力解决这个面试问题。
明确定义UTF-8格式后。例如:1字节: 0b0xxxxxxx 2-字节:....要求编写一个函数来验证是否 输入有效UTF-8。输入将是字符串/字节数组,输出 应该是/否。
我有两种可能的方法。
首先,如果输入是字符串,由于UTF-8最多为4个字节,在我们删除前两个字符“0b”之后,我们可以使用Integer.parseInt(s)来检查字符串的其余部分在0到10FFFF的范围内。此外,最好检查字符串的长度是否为8的倍数,以及输入字符串是否首先包含全0和1。所以我将不得不经历两次字符串,复杂性将是O(n)。
其次,如果输入是字节数组(如果输入是字符串,我们也可以使用此方法),我们检查每个1字节元素是否在正确的范围内。如果输入是一个字符串,首先检查字符串的长度是8的倍数然后检查每个8字符的子字符串是否在该范围内。
我知道有很多关于如何使用Java库检查字符串的解决方案,但我的问题是我应该如何根据问题实现该功能。
非常感谢。
答案 0 :(得分:13)
我们先来看看visual representation of the UTF-8 design。
现在让我们恢复我们必须做的事情。
x
字符代表实际的代码点。如果两个操作数都存在,我们将使用二进制AND运算符(&
)将结果复制到结果中。0b1xxxxxxx
进行按位操作,其中1将出现“序列中的字节”时间,其他位将为0. 该方法如下所示:
public static final boolean isUTF8(final byte[] pText) {
int expectedLength = 0;
for (int i = 0; i < pText.length; i++) {
if ((pText[i] & 0b10000000) == 0b00000000) {
expectedLength = 1;
} else if ((pText[i] & 0b11100000) == 0b11000000) {
expectedLength = 2;
} else if ((pText[i] & 0b11110000) == 0b11100000) {
expectedLength = 3;
} else if ((pText[i] & 0b11111000) == 0b11110000) {
expectedLength = 4;
} else if ((pText[i] & 0b11111100) == 0b11111000) {
expectedLength = 5;
} else if ((pText[i] & 0b11111110) == 0b11111100) {
expectedLength = 6;
} else {
return false;
}
while (--expectedLength > 0) {
if (++i >= pText.length) {
return false;
}
if ((pText[i] & 0b11000000) != 0b10000000) {
return false;
}
}
}
return true;
}
修改:实际方法不是原始方法(差不多但不是),并且是从here中窃取的。根据@EJP评论原来的那个没有正常工作。
答案 1 :(得分:3)
现实世界UTF-8兼容性检查的小解决方案:
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);
}
您可以查看测试结果:
@Test
public void testEnconding() {
byte[] invalidUTF8Bytes1 = new byte[]{(byte)0b10001111, (byte)0b10111111 };
byte[] invalidUTF8Bytes2 = new byte[]{(byte)0b10101010, (byte)0b00111111 };
byte[] validUTF8Bytes1 = new byte[]{(byte)0b11001111, (byte)0b10111111 };
byte[] validUTF8Bytes2 = new byte[]{(byte)0b11101111, (byte)0b10101010, (byte)0b10111111 };
assertThat(isUTF8(invalidUTF8Bytes1)).isFalse();
assertThat(isUTF8(invalidUTF8Bytes2)).isFalse();
assertThat(isUTF8(validUTF8Bytes1)).isTrue();
assertThat(isUTF8(validUTF8Bytes2)).isTrue();
assertThat(isUTF8("\u24b6".getBytes(StandardCharsets.UTF_8))).isTrue();
}
从https://codereview.stackexchange.com/questions/59428/validating-utf-8-byte-array
复制测试用例答案 2 :(得分:1)
public static boolean validUTF8(byte[] input) {
int i = 0;
// Check for BOM
if (input.length >= 3 && (input[0] & 0xFF) == 0xEF
&& (input[1] & 0xFF) == 0xBB & (input[2] & 0xFF) == 0xBF) {
i = 3;
}
int end;
for (int j = input.length; i < j; ++i) {
int octet = input[i];
if ((octet & 0x80) == 0) {
continue; // ASCII
}
// Check for UTF-8 leading byte
if ((octet & 0xE0) == 0xC0) {
end = i + 1;
} else if ((octet & 0xF0) == 0xE0) {
end = i + 2;
} else if ((octet & 0xF8) == 0xF0) {
end = i + 3;
} else {
// Java only supports BMP so 3 is max
return false;
}
while (i < end) {
i++;
octet = input[i];
if ((octet & 0xC0) != 0x80) {
// Not a valid trailing byte
return false;
}
}
}
return true;
}
答案 3 :(得分:0)
嗯,我很感谢评论和答案。 首先,我必须同意这是另一个愚蠢的面试问题&#34;。确实,在Java中,String已经被编码,因此它始终与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;
}
但是,由于所有可打印的字符串都是unicode形式,所以我没有机会得到错误。
其次,如果给定一个字节数组,它将始终在-2 ^ 7(0b10000000)到2 ^ 7(0b1111111)的范围内,因此它将始终处于有效的UTF-8范围内。
我对问题的初步理解是,给定一个字符串,说&#34; 0b11111111&#34;,检查它是否是有效的UTF-8,我想我错了。
此外,Java确实提供了将字节数组转换为字符串的构造函数,如果您对解码方法感兴趣,请检查here。
还有一件事,上面的答案对于另一种语言是正确的。唯一的改进可能是:
2003年11月,UTF-8受到RFC 3629的限制,以U + 10FFFF结束,以匹配UTF-16字符编码的限制。这删除了所有5字节和6字节序列,以及大约一半的4字节序列。
所以4个字节就足够了。
我绝对是这样的,如果我错了,请纠正我。非常感谢。
答案 4 :(得分:-1)
CharsetDecoder
可能就是你要找的东西:
@Test
public void testUTF8() throws CharacterCodingException {
// the desired charset
final Charset UTF8 = Charset.forName("UTF-8");
// prepare decoder
final CharsetDecoder decoder = UTF8.newDecoder();
decoder.onMalformedInput(CodingErrorAction.REPORT);
decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
byte[] bytes = new byte[48];
new Random().nextBytes(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes);
try {
decoder.decode(buffer);
fail("Should not be UTF-8");
} catch (final CharacterCodingException e) {
// noop, the test should fail here
}
final String string = "hallo welt!";
bytes = string.getBytes(UTF8);
buffer = ByteBuffer.wrap(bytes);
final String result = decoder.decode(buffer).toString();
assertEquals(string, result);
}
所以你的功能可能是这样的:
public static boolean checkEncoding(final byte[] bytes, final String encoding) {
final CharsetDecoder decoder = Charset.forName(encoding).newDecoder();
decoder.onMalformedInput(CodingErrorAction.REPORT);
decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
try {
decoder.decode(buffer);
return true;
} catch (final CharacterCodingException e) {
return false;
}
}