任意进制之间的转换
有一些进制转换的练习,例如10进制数转换为2进制数,或者反过来。闲来无事,用Rust
写了一个任意进制互相转换的代码。
#[derive(Debug, PartialEq, Eq)]
// 定义几种错误情况
pub enum Error {
InvalidInputBase, // 输入进制不符合要求
InvalidOutputBase, // 输出进制不符合要求
InvalidDigit(u32), // 输入字串出现了大于 from_base 的字符
}
// 定义几种常见的进制前缀
const BASE2: &str = "0b";
const BASE8: &str = "0o";
const BASE16: &str = "0x";
// 任意进制之间的转换
pub fn convert(number_str: &str, from_base: u32, to_base: u32) -> Result<String, Error> {
// 排除两种基数输入错误情况
if from_base <= 1 {
return Err(Error::InvalidInputBase);
};
if to_base <= 1 || to_base > 61 {
return Err(Error::InvalidOutputBase);
}
// 首先将输入的字符串转换为 十进制 数字
let mut number = 0_i128;
for (digit, num_char) in number_str.chars().rev().enumerate() {
// 将 num_char 从 char 类型转换为 数值类型
let num = match num_char {
('0'..='9') => (0..=9).zip('0'..='9').find(|(_, b)| *b == num_char).unwrap().0,
('A'..='Z') => (10..=35).zip('A'..='Z').find(|(_, b)| *b == num_char).unwrap().0,
('a'..='z') => (36..=61).zip('a'..='z').find(|(_, b)| *b == num_char).unwrap().0,
_ => panic!("Invalid input!"),
} as u32;
if num >= from_base {
return Err(Error::InvalidDigit(num));
}
number += num as i128 * (from_base as i128).pow(digit as u32);
}
if number == 0 {
return Ok("0".into());
}
let prefix: String = match to_base {
2 => BASE2.into(),
8 => BASE8.into(),
16 => BASE16.into(),
_ => "".into(),
};
// remainder 用于保存取余的结果
let mut remainder = Vec::new();
while number > 0 {
remainder.push(number % (to_base as i128));
number /= to_base as i128;
}
// 将余数逆序
remainder.reverse();
let res: String = remainder.iter().fold(prefix, |mut acc, x| {
match x {
x @ (10..=35) => acc.push(('A'..='Z').nth((x - 10) as usize).unwrap()),
x @ (36..=61) => acc.push(('a'..='z').nth((x - 36) as usize).unwrap()),
_ => acc.push_str(&x.to_string()),
}
acc
});
Ok(res)
}
思路:
from_base
代表输入的字符串代表的数字的进制,将输入字符串统一转换为十进制表示的数字,然后再将十进制数字通过取余的方法得到需要进制的数字。
步骤:
- 将输入的字符串解析为数字,因为存在
A
~z
英文字母,需要转换为对应的数字,使用
(10..=35).zip('A'..='Z').find(|(_, b)| *b == num_char).unwrap().0,
进行转换 - 在循环转换字符到数字的同时,进行相加,得到十进制数
- 最后再将十进制数转换为要求输出的进制,同样的,如果得到的余数大于9,那么用对应的
英文字母替代
代码解析:
- 对于
'0' ~ '9'
之间的字符,用zip
结合find
函数,得到对应的数字表示 - 对于
> '9'
的字符,同样结合zip
和find
得到索引,只不过要加上基数10
例如十六进制表示中,A
代表的数字是10
,以此类推 - 对于
> 'Z'
的字符,基数就是10
再加26
个字母,也就是36
- 限制小写字母
z
为最大的数字字符,因为在ascii
码表中,z
在英文字母范围内是最大的。z
对应的索引下标为25
,25 + 36 = 61
,所以规定输出的进制最大为61
进制
在转换的过程中,定义了常见的进制前缀,添加到了结果字符串中。
本人水平有限,有不当的地方,欢迎大家批评指正。