详细地演示gb18030到unicode|utf8的转码过程(RUST语言)

编码的预备知识

首先看一下保存在文件中的GB18030编码:

可见GB18030也涵盖了日语的汉字。 B9 E2 A4 C8 E9 9C 2C B9 E2 D3 EB D3 B0 => 光と闇,光与影 这中间小于0x7F的byte只有一个2C,这个2C应该就是逗号了。GB18030的编码规律:

字节长度码位空间码位数目
单字节0x00~0x80129
双字节字节1:0x81-0xFE字节2: 0x40-0x7E,0x80-0xFE23940
四字节字节1:0x81-0xFE字节2:0x30-0x39字节3:0x81-0xFE字节4:0x30-0x391587600

规律:

  1. 单字节部分和ASCII和Unicode一致。
  2. 双字节第一个和四字节第一个,四字节第三个范围一致,但是后续一位的取值范围没有重合,因此可以准确区分2,4字节条目。
  3. 双字节的第二位没有7F,所以在查表的时候需要给予考虑。

RUST的预备知识

  • 读取文件的字节,为了处理无限长的序列,开用iterator的方式使用文件。
let mut f = File::open("foo.txt")?;
f.bytes()

  • 根据字节内容分离出字符。有可能是1,2,4字节。
type TripleOptionU8 = (Option<u8>, Option<u8>, Option<u8>);

#[derive(Debug)]
pub enum MayBeMatchError {
    Continue,
    Impossible,
    Discard(Vec<u8>),
}

type CharResult = Result<char, MayBeMatchError>;

pub fn try_get_char(state: &mut TripleOptionU8, current_byte: u8) -> CharResult {
    match *state { // we didn't move or copy it.
        (None, None, None) => match current_byte {
            0x00...0x7F => Ok(char::from_u32(u32::from(current_byte)).unwrap()), // state keep empty.
            0x80 => Ok(char::from_u32(0x20AC).unwrap()), // state keep empty.
            _ => {
                state.0 = Some(current_byte);
                Err(MayBeMatchError::Continue)
            }
        },
        (Some(b1), None, None) => match b1 { // exam the first byte.
            0x81...0xFE => match current_byte {
                0x40...0x7E | 0x80...0xFE => { // a valid gb18030
                    let low_boundary = if b1 > 0x7E {0x41} else {0x40};
                    let index = u32::from(b1 - 0x81) * 190 + u32::from(current_byte - low_boundary);
                    let cp = GBK_UNI[index as usize];
                    debug!("got index: {}, codepoint: 0x{:X}", index, cp);
                    state.0 = None;
                    Ok(char::from_u32(cp).unwrap())
                },
                0x30...0x39 => { // maybe a four byte gb18030
                    state.1 = Some(current_byte);
                    Err(MayBeMatchError::Continue)
                },
                _ => { // the current byte is not valid. discard it.
                    state.0 = None;
                    Err(MayBeMatchError::Discard(vec![b1]))
                }
            },
            _ => { // the first byte is invalid. discard it.
                state.0 = None;
                Err(MayBeMatchError::Discard(vec![b1]))
            }
        },
        (Some(b1), Some(b2), None) => match current_byte { // b2 is always valid.
            0x81...0xFE => {
                state.2 = Some(current_byte);
                Err(MayBeMatchError::Continue)
            },
            _ => {
                *state = (None, None, None);
                Err(MayBeMatchError::Discard(vec![b1, b2, current_byte]))
            }
        },
        (Some(b1), Some(b2), Some(b3)) => match current_byte {
            0x30...0x39 => {
                *state = (None, None, None);
                let cp = gb18030_4b_to_code_point([b1, b2, b3, current_byte]).unwrap();
                Ok(char::from_u32(cp).unwrap())
            },
            _ => {
                *state = (None, None, None);
                Err(MayBeMatchError::Discard(vec![b1, b2, b3, current_byte]))
            }
        },
        _ => Err(MayBeMatchError::Continue)
    }
}

  • 收集成一个String。
        let f = File::open(path).unwrap();
        let mut trio = (None, None, None);
        let cs: String = f.bytes()
            .map(|ob|ob.unwrap())
            .map(|b|try_get_char(&mut trio, b))
            .filter(Result::is_ok)
            .flat_map(|c|c)
            .collect()

查询表的准备(仅对双字节)

原材料:http://icu-project.org/repos/icu/data/trunk/charset/source/gb18030/ 以下是以GB18030以升序排序的内容:

8140:4E02
8141:4E04
8142:4E05
8143:4E06

根据码表的规则,8140显然是排在第一个。如何计算偏移呢?

  • 第一位的范围是:0x81-0xFE + 1 = 126个。
  • 第二位的范围是:0x40-0x7E,0x80-0xFE => 0xFE - 0x40 + 1 - 1 = 190,因为中间少了一个7F必须减去。也可以采用另一种方法,将任意值填入7F出现的126个位置。这样公式就不用判断第二位是否大于0x7E。

转载于:https://my.oschina.net/jianglibo/blog/3012922

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值