P2395 BBCode转换Markdown 题解
Eleveslaine · · 题解
前言
码量适中的大模拟,作为我(蒟蒻)的第一道紫题来写个题解。(逃
思维难度只有普及,代码难度较大但是显然没有猪国杀难。
分析&代码
从宏观视角来说,本题就是给你一段 BBCode,然后让你转化成 Markdown。
显然有以下思路:一次全部输入完,从头遍历一遍并匹配 [h1],[h2],[/h1] 等标签进行处理,处理过程使用一个栈 st 来应对可能出现的 Match Error 或 Unclosed Mark 错误。
设目前遍历到的位置为 pointer,初始为
- 如果
s 从pointer开始的一个前缀为[h1],对应地处理[h1],pointer向后跳4 位(即跳到[h1]后面的第一个字符); - 以此类推,如果
s 从pointer开始的一个前缀为p ,对应地处理p ,pointer后移|p| 位。 - ……
- 如果前面都没有匹配上,说明现在
pointer的位置只是一个普通字符,输出这个字符并pointer++即可。
给出此部分代码实现(细节放在代码注释中):
// 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;
}
......
除了上文直接替换的标签,还存在一些特殊标签需要特殊处理:
[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="))
{
// 
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();
}
[quote]标签。
这个标签可以说是本题中最难处理的了,主要原因是本题对此表述十分不清晰。它的处理过程如下:
因为它中间的所有标签不用渲染,所以可以一次从[quote]读到结束标签[/quote]。
- 过滤内容前后的
\n,并在最前面插入 ">"。 - 对于中间的每个
\n,在其后插入 ">"。 - 其他内容不变。
(注:不考虑\r,在输入时已经过滤一遍。)
此外,由于在读[quote]时已经读完了[/quote],所以如果还存在落单的[/quote]那么它前面一定没有[quote],直接判为 Match Error 即可。
这里的代码实现见下面剪贴板中if(pd(s,"[quote]"))部分。
全部处理完后,如果栈不为空,判定 Unclosed Mark。最后如果不存在错误再进行输出。
完整代码见剪贴板。
致谢
感谢大模拟之神 @qwasd 提供代码优化方面的帮助,%%% Orz!