P2395 BBCode转换Markdown 题解

· · 题解

前言

码量适中的大模拟,作为我(蒟蒻)的第一道紫题来写个题解。(逃
思维难度只有普及,代码难度较大但是显然没有猪国杀难

分析&代码

从宏观视角来说,本题就是给你一段 BBCode,然后让你转化成 Markdown。

显然有以下思路:一次全部输入完,从头遍历一遍并匹配 [h1][h2][/h1] 等标签进行处理,处理过程使用一个栈 st 来应对可能出现的 Match Error 或 Unclosed Mark 错误。
设目前遍历到的位置为 pointer,初始为 0

给出此部分代码实现(细节放在代码注释中):

// inline bool pd(const string &s,const string &pre)
// 判断 s 从 pointer 开始的前缀是否为 pre
while(pointer<s.length())
    if(pd(s,"[h1]"))
    {
        // st 为自定义 Node 类型的栈
        // Node 有两个属性:int type 和 string info
        // 前者代表标签类型(例如 [h1] 标签 type 为 1),
        // 后者存储其他信息,用于 [url] 和 [img] 标签
        st.push((Node){1,""});
        out("# ");
        pointer+=4;
    }
    else if(pd(s,"[h2]"))
    {
        st.push((Node){2,""});
        out("## ");
        pointer+=4;
    }
    else if(pd(s,"[i]"))
    {
        st.push((Node){3,""});
        out("*");
        pointer+=3;
    }
    else if(pd(s,"[b]"))
    {
        st.push((Node){4,""});
        out("__");
        pointer+=3;
    }
    else if(pd(s,"[/h1]"))
    {
        // MatchError 宏定义,字面意思,触发错误并 break;
        if(st.empty()||st.top()!=1)
            MatchError
        st.pop();
        out(" #");
        pointer+=5;
    }
    else if(pd(s,"[/h2]"))
    {
        if(st.empty()||st.top()!=2)
            MatchError
        st.pop();
        out(" ##");
        pointer+=5;
    }
    else if(pd(s,"[/i]"))
    {
        if(st.empty()||st.top()!=3)
            MatchError
        st.pop();
        out("*");
        pointer+=4;
    }
    else if(pd(s,"[/b]"))
    {
        if(st.empty()||st.top()!=4)
            MatchError
        st.pop();
        out("__");
        pointer+=4;
    }
    ......

除了上文直接替换的标签,还存在一些特殊标签需要特殊处理:

  1. [url][img] 标签。
    url 为例,它的标准格式为 [url=A]B[/url],识别时的固定前缀为 [url=。之后找到第一个 ] 字符(本题保证 A 中没有 ]),由此截下 A 部分的内容,随着 Node 存入栈中,并先输出一个 [ 字符;
    中间的 B 可能还含有标签,留给其他部分处理;
    等识别到 [/url] 结束标签时,从栈中拿出 A,输出 ](A),结束。
    [img] 标签同理。
    代码实现:
......
else if(pd(s,"[url="))
{
    // [text](url)
    url=s.substr(pointer+5,s.find_first_of(']',pointer)-pointer-5);
    pointer+=6+url.length();
    st.push((Node){6,url});
    out("[");
}
else if(pd(s,"[/url]"))
{
    if(st.empty()||st.top()!=6)
        MatchError
    out("](");
    out(st.top().info.c_str());
    out(")");
    pointer+=6;
    st.pop();
}
else if(pd(s,"[img="))
{
    // ![text](img)
    img=s.substr(pointer+5,s.find_first_of(']',pointer)-pointer-5);
    pointer+=6+img.length();
    st.push((Node){7,img});
    out("![");
}
else if(pd(s,"[/img]"))
{
    if(st.empty()||st.top()!=7)
        MatchError
    out("](");
    out(st.top().info.c_str());
    out(")");
    pointer+=6;
    st.pop();
}
  1. [quote] 标签。
    这个标签可以说是本题中最难处理的了,主要原因是本题对此表述十分不清晰。它的处理过程如下:
    因为它中间的所有标签不用渲染,所以可以一次从 [quote] 读到结束标签 [/quote]

全部处理完后,如果栈不为空,判定 Unclosed Mark。最后如果不存在错误再进行输出。

完整代码见剪贴板。

致谢

感谢大模拟之神 @qwasd 提供代码优化方面的帮助,%%% Orz!