You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中多行字符串正则无法捕获全部代码块问题求助

问题原因分析
  1. 正则匹配逻辑问题:你当前的正则表达式大概率没启用全局匹配,或是用了贪婪模式(比如([\s\S]+)而非非贪婪的([\s\S]+?)),导致第一个匹配就吞噬了后续所有内容,只能捕获第一个代码块。另外,如果正则没正确处理代码块前后的换行或文本边界,也会导致匹配范围错误,让高亮延续到普通文本。
  2. 高亮处理逻辑缺陷:如果你的代码只处理了第一个匹配结果,没循环遍历所有捕获到的代码块,自然不会处理后续的。另外,若没有在处理完一个代码块后重置高亮状态(或是错误地将整个文本当作单一代码块处理),会导致普通文本被错误应用代码高亮规则。
解决方案

1. 修正正则表达式并全局匹配

使用支持全局匹配的正则,确保能捕获所有代码块。示例正则如下:

use regex::Regex;

// 匹配带语言标识的代码块,非贪婪模式,支持全局匹配
let code_block_re = Regex::new(r"```([a-z0-9]+)\n([\s\S]+?)```").unwrap();

这里的([\s\S]+?)是非贪婪匹配,会在遇到第一个```时停止,避免吞噬后续内容。

2. 拆分文本并分别处理

遍历所有匹配的代码块,将原文本拆分为「普通文本段」和「代码块段」,对普通文本直接保留,代码块用syntect高亮后替换,最后拼接所有片段:

use syntect::{
    easy::HighlightLines,
    highlighting::{Style, ThemeSet},
    parsing::SyntaxSet,
    util::{as_24_bit_terminal_escaped, LinesWithEndings},
};

fn highlight_markdown_code_blocks(input: &str) -> String {
    let syntax_set = SyntaxSet::load_defaults_newlines();
    let theme = &ThemeSet::load_defaults().themes["base16-ocean.dark"];
    let code_block_re = Regex::new(r"```([a-z0-9]+)\n([\s\S]+?)```").unwrap();

    let mut result = String::new();
    let mut last_end = 0;

    // 遍历所有匹配的代码块
    for cap in code_block_re.captures_iter(input) {
        // 添加匹配前的普通文本
        result.push_str(&input[last_end..cap.get(0).unwrap().start()]);
        
        let lang = cap.get(1).unwrap().as_str();
        let code = cap.get(2).unwrap().as_str();
        
        // 获取对应语言的语法,找不到则用plaintext兜底
        let syntax = syntax_set.find_syntax_by_name(lang).unwrap_or_else(|| {
            syntax_set.find_syntax_by_name("plaintext").unwrap()
        });
        
        // 高亮代码块
        let mut highlighter = HighlightLines::new(syntax, theme);
        let mut highlighted_code = String::new();
        for line in LinesWithEndings::from(code) {
            let ranges: Vec<(Style, &str)> = highlighter.highlight_line(line, &syntax_set).unwrap();
            highlighted_code.push_str(&as_24_bit_terminal_escaped(&ranges[..], true));
        }
        
        // 重新包裹代码块格式(可根据需求调整)
        result.push_str(&format!("```{}", lang));
        result.push_str(&highlighted_code);
        result.push_str("```");
        
        last_end = cap.get(0).unwrap().end();
    }

    // 添加最后一段普通文本
    result.push_str(&input[last_end..]);
    result
}

3. 替代实现:用专业Markdown解析库

正则处理Markdown代码块容易遇到边缘情况(比如代码块内包含```、嵌套代码块等),更可靠的方式是用专门的Markdown解析库,比如pulldown-cmark:

use pulldown_cmark::{Event, Options, Parser, Tag};
use syntect::{
    easy::HighlightLines,
    highlighting::{Style, ThemeSet},
    parsing::SyntaxSet,
    util::{as_24_bit_terminal_escaped, LinesWithEndings},
};

fn highlight_markdown_code_blocks(input: &str) -> String {
    let syntax_set = SyntaxSet::load_defaults_newlines();
    let theme = &ThemeSet::load_defaults().themes["base16-ocean.dark"];
    
    let mut options = Options::empty();
    options.insert(Options::ENABLE_STRIKETHROUGH);
    let parser = Parser::new_ext(input, options);
    
    let mut result = String::new();
    let mut in_code_block = false;
    let mut current_syntax = None;
    let mut current_code = String::new();
    
    for event in parser {
        match event {
            Event::Start(Tag::CodeBlock(lang)) => {
                in_code_block = true;
                // 解析语言标识,找不到则用plaintext
                let lang_str = lang.as_ref().map(|s| s.as_str()).unwrap_or("plaintext");
                current_syntax = syntax_set.find_syntax_by_name(lang_str)
                    .or_else(|| syntax_set.find_syntax_by_name("plaintext"));
            }
            Event::End(Tag::CodeBlock(_)) => {
                in_code_block = false;
                // 高亮代码并添加到结果
                if let Some(syntax) = current_syntax {
                    let mut highlighter = HighlightLines::new(syntax, theme);
                    let mut highlighted_code = String::new();
                    for line in LinesWithEndings::from(&current_code) {
                        let ranges = highlighter.highlight_line(line, &syntax_set).unwrap();
                        highlighted_code.push_str(&as_24_bit_terminal_escaped(&ranges[..], true));
                    }
                    result.push_str(&format!("```{}", lang.as_ref().unwrap_or(&"plaintext".into())));
                    result.push_str(&highlighted_code);
                    result.push_str("```\n");
                }
                current_code.clear();
                current_syntax = None;
            }
            Event::Text(text) if in_code_block => {
                current_code.push_str(&text);
            }
            Event::Text(text) => {
                result.push_str(&text);
            }
            // 其他Markdown元素(标题、列表等)可按需保留格式
            _ => {}
        }
    }
    result
}
测试验证

用以下测试文本验证:

这是普通文本
```rust
fn main() {
    println!("Hello World!");
}

这是中间的普通文本

print("Hello Python")

这是最后一段普通文本

修正后的代码应该能正确高亮两个代码块,且中间和末尾的普通文本不会被错误高亮。

内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 06:08:13