编码的预备知识
首先看一下保存在文件中的GB18030编码:
可见GB18030也涵盖了日语的汉字。 B9 E2 A4 C8 E9 9C 2C B9 E2 D3 EB D3 B0 => 光と闇,光与影
这中间小于0x7F的byte只有一个2C,这个2C应该就是逗号了。GB18030的编码规律:
字节长度 | 码位空间 | 码位数目 | |||
---|---|---|---|---|---|
单字节 | 0x00~0x80 | 129 | |||
双字节 | 字节1:0x81-0xFE | 字节2: 0x40-0x7E,0x80-0xFE | 23940 | ||
四字节 | 字节1:0x81-0xFE | 字节2:0x30-0x39 | 字节3:0x81-0xFE | 字节4:0x30-0x39 | 1587600 |
规律:
- 单字节部分和ASCII和Unicode一致。
- 双字节第一个和四字节第一个,四字节第三个范围一致,但是后续一位的取值范围没有重合,因此可以准确区分2,4字节条目。
- 双字节的第二位没有7F,所以在查表的时候需要给予考虑。
RUST的预备知识
- 读取文件的字节,为了处理无限长的序列,开用iterator的方式使用文件。
let mut f = File::open("foo.txt")?;f.bytes()
- 根据字节内容分离出字符。有可能是1,2,4字节。
type TripleOptionU8 = (Option, Option , Option );#[derive(Debug)]pub enum MayBeMatchError { Continue, Impossible, Discard(Vec ),}type CharResult = Result ;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()
查询表的准备(仅对双字节)
原材料: 以下是以GB18030以升序排序的内容:
8140:4E028141:4E048142:4E058143:4E06
根据码表的规则,8140显然是排在第一个。如何计算偏移呢?
- 第一位的范围是:0x81-0xFE + 1 = 126个。
- 第二位的范围是:0x40-0x7E,0x80-0xFE => 0xFE - 0x40 + 1 - 1 = 190,因为中间少了一个7F必须减去。也可以采用另一种方法,将任意值填入7F出现的126个位置。这样公式就不用判断第二位是否大于0x7E。