功能说明
哈希摘要、协议调试和二进制配置常以十六进制文本表示。编码方向把每个字节拆成高四位和低四位,查表转换为小写字符;解码方向接受带 0x 前缀、包含空格或换行的输入,先过滤空白再两位一组转换,遇到非法字符给出明确错误。
示例用 hello 演示往返,另外测试带前缀的 DE AD BE EF、奇数长度与非法字符。输出使用 from_utf8_lossy 展示解码后的文本,实际二进制数据应直接保留字节。
关键代码
encode_hex 预分配两倍长度的字符串,逐字节查表,避免格式化开销。hex_value 用模式匹配把数字、大小写字母映射到零到十五。decode_hex 先 trim 并去掉可选前缀,再过滤所有 ASCII 空白,长度必须为偶数,然后用 chunks 每两个字符一组计算并合并。错误信息保留原始字符,便于定位输入问题。
使用注意
解码会忽略所有空白,包括字符串中间的换行,这对粘贴多行摘要很方便,但也意味着无法区分带空格的二进制格式。编码结果固定小写,比较时应统一大小写。0x 前缀只在最外层识别一次,中间出现会报错。超长输入会一次性分配两倍内存,大文件应分块处理。十六进制不是加密,不能用于隐藏敏感数据。
验证方式
hello 编码为 68656c6c6f,解码后还原为 hello。带前缀且含空格的 DE AD BE EF 解码为四字节。abc 因长度为奇数返回错误,zz 因包含非法字符返回错误,两个提示均为 true。可以把大写输入再编码,确认输出统一为小写。
代码示例
fn encode_hex(bytes: &[u8]) -> String {
const DIGITS: &[u8; 16] = b"0123456789abcdef";
let mut output = String::with_capacity(bytes.len() * 2);
for &byte in bytes {
output.push(DIGITS[(byte >> 4) as usize] as char);
output.push(DIGITS[(byte & 0x0f) as usize] as char);
}
output
}
fn hex_value(byte: u8) -> Result<u8, String> {
match byte {
b'0'..=b'9' => Ok(byte - b'0'),
b'a'..=b'f' => Ok(byte - b'a' + 10),
b'A'..=b'F' => Ok(byte - b'A' + 10),
_ => Err(format!("invalid hex digit: {}", byte as char)),
}
}
fn decode_hex(input: &str) -> Result<Vec<u8>, String> {
let trimmed = input.trim();
let cleaned = trimmed
.strip_prefix("0x")
.or_else(|| trimmed.strip_prefix("0X"))
.unwrap_or(trimmed);
let digits: Vec<u8> = cleaned
.bytes()
.filter(|byte| !byte.is_ascii_whitespace())
.collect();
if digits.len() % 2 != 0 {
return Err("hex input must have an even number of digits".to_string());
}
let mut output = Vec::with_capacity(digits.len() / 2);
for pair in digits.chunks(2) {
let high = hex_value(pair[0])?;
let low = hex_value(pair[1])?;
output.push((high << 4) | low);
}
Ok(output)
}
fn main() {
let payload = b"hello";
let encoded = encode_hex(payload);
println!("encoded: {encoded}");
println!(
"decoded: {:?}",
decode_hex(&encoded).map(|bytes| String::from_utf8_lossy(&bytes).into_owned())
);
println!("prefixed: {:?}", decode_hex("0xDE AD BE EF"));
println!("odd length: {}", decode_hex("abc").is_err());
println!("bad digit: {}", decode_hex("zz").is_err());
}