← 中文资源

简体中文版本

游程编码压缩重复字符

把连续重复字符编码为计数加字符,并在解码时拒绝零计数和悬空数字。

阅读英文原文

功能说明

游程编码(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(&current) {
            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());
}