功能说明
游程编码(Run-Length Encoding,RLE)把连续重复的字符替换为“次数 + 字符”,例如 aaabccdddd 变成 3ab2c4d,其中只出现一次的 b 保持原样。它实现简单、解码快,适合图标位图、扫描线数据、日志中大量重复空白的场景。
这段代码同时提供编码和解码,并在解码时检查零计数、非法数字和结尾悬空数字,避免把损坏输入静默解码成错误内容。
关键代码
encode 用 Peekable<Chars> 逐个读取字符,内层循环在下一个字符相同时持续计数。计数大于 1 时才写入数字,单个字符保持原样,减少输出体积。
decode 累积连续 ASCII 数字,遇到非数字字符时把数字解析成 usize;没有数字则默认次数为 1。随后用 repeat(...).take(count) 生成重复字符。循环结束后如果还有未消费的数字,说明输入以计数结尾,返回错误。
使用注意
这种格式无法表示原始文本中的数字字符:12 会被当成数字前缀而不是两个字面字符。实际使用时通常限定输入字母表,或引入转义符。解码未限制单次计数和总输出大小,恶意输入如 999999999a 可能造成内存耗尽,生产代码必须设置上限。
编码使用字符而不是字节,对 Unicode 友好,但计数和字符之间没有分隔符,非 ASCII 数字不会被当作计数。RLE 对随机数据可能膨胀,应在编码后比较长度再决定是否采用。
验证方式
示例对 aaabccdddd、abc 和十二个 w 编码再解码,结果都与原文一致;aaabccdddd 的编码精确等于 3ab2c4d,单字符不写次数。0a 因计数为零被拒绝,12 因数字后没有字符被拒绝。可以再加入 1a 和 2é,确认单字符与 Unicode 路径正常。
代码示例
fn encode(input: &str) -> String {
let mut output = String::new();
let mut characters = input.chars().peekable();
while let Some(current) = characters.next() {
let mut count = 1usize;
while characters.peek() == Some(¤t) {
characters.next();
count += 1;
}
if count > 1 {
output.push_str(&count.to_string());
}
output.push(current);
}
output
}
fn decode(input: &str) -> Result<String, String> {
let mut output = String::new();
let mut digits = String::new();
for character in input.chars() {
if character.is_ascii_digit() {
digits.push(character);
continue;
}
let count = if digits.is_empty() {
1
} else {
digits
.parse::<usize>()
.map_err(|_| format!("invalid count: {digits}"))?
};
if count == 0 {
return Err("count must be positive".to_string());
}
output.extend(std::iter::repeat(character).take(count));
digits.clear();
}
if !digits.is_empty() {
return Err("trailing count without a character".to_string());
}
Ok(output)
}
fn main() {
for sample in ["aaabccdddd", "abc", "wwwwwwwwwwww"] {
let encoded = encode(sample);
let decoded = decode(&encoded).expect("valid encoding");
println!("{sample} -> {encoded} -> {decoded}");
assert_eq!(decoded, sample);
}
assert_eq!(encode("aaabccdddd"), "3ab2c4d");
assert!(decode("0a").is_err());
assert!(decode("12").is_err());
}