leetcode/
utf8_validation.rs

1//! # 393. UTF-8 编码验证
2//!
3//! 难度 中等
4//!
5//! UTF-8 中的一个字符可能的长度为 1 到 4 字节,遵循以下的规则:
6//!
7//! 1. 对于 1 字节的字符,字节的第一位设为0,后面7位为这个符号的unicode码。
8//! 2. 对于 n 字节的字符 (n > 1),第一个字节的前 n 位都设为1,第 n+1 位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
9//!
10//! 这是 UTF-8 编码的工作方式:
11//!
12//! ```text
13//!    Char. number range  |        UTF-8 octet sequence
14//!       (hexadecimal)    |              (binary)
15//!    --------------------+---------------------------------------------
16//!    0000 0000-0000 007F | 0xxxxxxx
17//!    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
18//!    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
19//!    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
20//! ```
21//!
22//! 给定一个表示数据的整数数组,返回它是否为有效的 utf-8 编码。
23//!
24//! ## 注意:
25//!
26//! 输入是整数数组。只有每个整数的最低 8 个有效位用来存储数据。这意味着每个整数只表示 1 字节的数据。
27//!
28//! ## 示例 1:
29//!
30//! ```text
31//! data = [197, 130, 1], 表示 8 位的序列: 11000101 10000010 00000001.
32//!
33//! 返回 true 。
34//! 这是有效的 utf-8 编码,为一个2字节字符,跟着一个1字节字符。
35//! ```
36//!
37//! ## 示例 2:
38//!
39//! ```text
40//! data = [235, 140, 4], 表示 8 位的序列: 11101011 10001100 00000100.
41//!
42//! 返回 false 。
43//! 前 3 位都是 1 ,第 4 位为 0 表示它是一个3字节字符。
44//! 下一个字节是开头为 10 的延续字节,这是正确的。
45//! 但第二个延续字节不以 10 开头,所以是不符合规则的。
46//! ```
47//!
48//! See [leetcode](https://leetcode-cn.com/problems/utf-8-validation/)
49
50pub struct Solution;
51
52impl Solution {
53
54    /// 支持 Pattern Matching 的语言做这种事很简单。
55    ///
56    /// 另外 Rust 的 Iterator 抽象很方便。
57    ///
58    /// 每位检查如果符合规则,要 `continue` 继续检查剩余字节,直到所有字节都符合,返回 `true`。否则,检查过程中只要遇到不符合规则的,就直接返回 `false`。
59    ///
60    /// 低位直接检查是否符合就行,要注意先要判断高位的情况,即开头超过 4 个 1 的字节是不符合的。
61    pub fn valid_utf8(data: Vec<i32>) -> bool {
62        // 取低 8 位,根据题目输入数据的限制,`as u8` 也行。
63        // `fuse()` 保证到结尾后多次调用 `.next()` 依然返回 `None`
64        let mut t = data.iter().map(|&i| i.to_le_bytes()[0]).fuse();
65        let mut valid = true;
66        while let (Some(n), true) = (t.next(), valid) {
67            valid = match n {
68                // 前缀越界,因为 UTF-8 只能是 1 到 4 字节,前缀不可能是 5 位或以上的 1 开头
69                n if n & 0b1111_1000 == 0b1111_1000 => false,
70                // 4 字节情况,取后三位检查
71                n if n & 0b1111_0000 == 0b1111_0000 => match (t.next(), t.next(), t.next()) {
72                    (Some(n1), Some(n2), Some(n3)) if n1 & n2 & n3 & 0b1000_0000 == 0b1000_0000 => true,
73                    _ => false,
74                }
75                // 3 字节情况,取后两位检查
76                n if n & 0b1110_0000 == 0b1110_0000 => match (t.next(), t.next()) {
77                    (Some(n1), Some(n2)) if n1 & n2 & 0b1000_0000 == 0b1000_0000 => true,
78                    _ => false,
79                }
80                // 2 字节情况,取后一位检查
81                n if n & 0b1100_0000 == 0b1100_0000 => match t.next() {
82                    Some(n1) if n1 & 0b1000_0000 == 0b1000_0000 => true,
83                    _ => false,
84                }
85                // 1 字节情况
86                // 只要是 `0xxx_xxxx` 以 `0` 开头就 OK
87                // 注意,这里排除了 `10xx_xxxx` 开头的情况,这种情况是不符合规则的
88                n if n & 0b1000_0000 == 0 => true,
89                // 其它情况,均不符合规则
90                _ => false,
91            };
92        }
93
94        valid
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test() {
104        let cases = vec![
105            (true, vec![
106             vec![],
107             vec![197, 130, 1],
108            ]),
109
110            (false, vec![
111             vec![235, 140, 4],
112             vec![0b1111_1111, 130, 1],
113             vec![0b1100_0000, 0b0000_1111],
114             vec![250,145,145,145,145],
115             vec![248,130,130,130],
116             vec![0b1011_0000, 130, 130],
117            ]),
118        ];
119        for (expect, sub_cases) in cases {
120            sub_cases.into_iter().map(Solution::valid_utf8).for_each(|x| assert_eq!(expect, x));
121        }
122    }
123}