下面的章节会给出三个例子来解释题目,如果你很清楚这道题了,那你可以直接跳过。
题述中给了两个例子来帮助你理解 UTF-8 字符集的规则。对于很多人来说这两个例子是不够的,所以接下来做的第一件事就是来理解题述中给的规则,同时再来看一个例子来帮助我们更好地理解这道题。那就开始吧。
一个合法的 UTF-8 字符的长度为 1-4 字节。
对于 1 字节的字符,字节的第一位设为 0,后面 7 位为这个符号的 unicode 码。
对于 n 字节的字符 (n > 1),第一个字节的前 n 位都为 1,第 n+1 位为 0,后面字节的前两位一律都为 10。
输入是代表字符的整数数组。我们要判断数组中的数能不能表示成数个合法的 UTF-8 字符。在第一个的例子里面可以看到,数组上的可以表示称数个合法的 UTF-8 字符,因此这个数组是合法的。
有一个点需要注意的是,数组中的整数是有可能大于255的。我们都知道 8 比特能表示的最大整数为255。假如数组中有一个数为 476,那我们该怎么处理呢? 根据题述例子中的说明,我们只需要考虑每个整数中 最高8位 就可以了。
现在规则已经搞清楚了,让我们先来看题述中例子,接下里再看一些可能造成困惑的其他例子。
例一
data = [197, 130, 1]
首先把这个数组表示成二进制的形式
11000101 10000010 00000001
记住,对于一个 n 字节长的 UTF-8 字符来说,第一个字节的前 n 位都为 1,第 n+1 位为 0,后面字节的前两位一律都为 10。
[1 1 0] 0 0 1 0 1
↑ ↑
第一个字节的前两个比特值都是 1,1 后面紧接着一个 0。这是一个合法 UTF-8 字符的开始。同时从中可知这是一个 2 字节长的 UTF-8 字符。这也就意味着序列中接下来的字节一定是 10xxxxxx 这样的形式。
[1 1 0] 0 0 1 0 1 [1 0] 0 0 0 0 1 0
↑ ↑ ↑ ↑
是的,确实是遵循了规则。因此数组中 197 130 这两个数共同组成了一个合法的 2 字节 UTF-8 字符。既然数组中还有元素,继续以同样的方式检查。接下来的整数是 1,先把它变成二进制表示。
00000001
在这里最高比特位是 0,那么它只能满足 1 字节 UTF-8 字符的规则了。让我们来回顾一下这个规则:
对于 1 字节的字符,字节的第一位设为 0,后面 7 位为这个符号的 unicode 码。
[0] 0 0 0 0 0 0 1
↑
显然,整数 1 本身就是一个合法的 1 字节 UTF-8 字符。这时候数组已经没有剩余数要处理了,从前面的分析可知整个数组表示的是两个合法的 UTF-8 字符,因此需要返回 True。
例子二
[235, 140, 4]
这是题述中的第二个例子。照样先把这个整数数组的转成二进制表示。
11101011 10001100 00000100
从数组的第一个整数开始。第一个字节会告诉我们这个 UTF-8 字符的长度,同时会告诉我们接下里该处理多少个字节来完全解析当前的 UTF-8 字符。
[1 1 1 0] 1 0 1 1
↑ ↑
可以看到前四个比特为 1110。这意味着这个 UTF-8 字符总共有 3 字节。记住,我们可以从第一个字节中知晓这个潜在 UTF-8 字符的长度。
对于 n 字节的字符 (n > 1),第一个字节的前 n 位都为 1,第 n+1 位为 0。
根据这个规则我们知道了第一个 UTF-8 字符是长度为 3 字节。第一个字节已经处理过了,还需要处理剩下的两个字节。我们来看一下这个数组中剩下的两个字节。
[1 0] 0 0 1 1 0 0 0 0 0 0 0 1 0 0
↑ ↑ ↑ (WRONG!)
第一个字节遵循了 10xxxxxx 规则,但是第二个字节违反了这个规则。根据第一个字节 11101011,这只能是一个 3 字节 UTF-8 字符。但是最后一个字节违反了规则。因此,这是一个非法字节,这也就意味着可以直接返回 False了。
例三
在看题解之前我们来看最后一个例子。在讨论区可以看到这个例子对很多人造成了困惑。
下面就是这个例子了:
[250,145,145,145,145]
照旧把数组中所有的整数转成二进制表示。
11111010 10010001 10010001 10010001 10010001
和前两个例子一样,首先看第一个字节来获取当前这个 UTF-8 字符的长度。从第一个字节中可以知道这个 UTF-8 字符的长度是 5 字节。
[1 1 1 1 1 0] 1 0
↑ ↑
如果这是一个合法的 UTF-8 字符,那么接下来的四个字节必须遵守 10xxxxxx 规则。我们来看一下接下来的四个字节吧。
- [1 0] 0 1 0 0 0 1
- [1 0] 0 1 0 0 0 1
- [1 0] 0 1 0 0 0 1
- [1 0] 0 1 0 0 0 1
可以看到,这四个字节都是遵守 10xxxxxx 规则。那为什么这个例子需要返回 False 呢? 这是因为你忽略了题目给的第一个规则。
题述中的第一个规则,很清楚地说了 “一个合法的 UTF-8 字符的长度为 1-4 字节。”
从第一个字节就知道了这个 UTF-8 字符将包含 5 字节 了,这也就意味着这个字符绝对是非法的。这也就是为什么对于这个例子应该返回 False了。
作者:LeetCode
链接:https://leetcode-cn.com/problems/utf-8-validation/solution/utf-8-bian-ma-yan-zheng-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
bool validUtf8(vector<int>& data) {
int p=0,cnt;
while(p<data.size())
{
cnt=0;
//通过与运算确定首个字节蕴含的字节长度
if((data[p]&0b10000000)==0)
{
cnt=0;
}
else if((data[p]&0b11100000)==0b11000000)
{
cnt=1;
}
else if((data[p]&0b11110000)==0b11100000)
{
cnt=2;
}
else if((data[p]&0b11111000)==0b11110000)cnt=3;
//0b后面的比data[p]&x 中的1少一位
else
return false;
p++;
//本身首个8位占一次,p+1
while(p<data.size()&&cnt)
//某一个原数读取完毕,则cnt=0,p是关于原数所在数组中的位置
{
if((data[p]&0b11000000)!=0b10000000) break;
p++;
cnt--;
}
//a1 b1 b1 a2 b2 b2 b2这种情况,读完b1,cnt清零,p不清零
if(cnt!=0) return false;
//cnt没清零,出现错误
}
return true;
//所有情况都通过,返回true
}
};