P2395 BBCode转换Markdown 题解
Untitled10032 · · 题解
这是一篇使用 C 语言的题解。
简要题意
- 将(题目中给出的)BBCode 风格的标签转换为 Markdown 格式,并实现一定的语法检测功能。
思路
一道中模拟题。可比 P3695 什么的阳间多了。
他要什么你就怎么写就可以了。
首先是读入。
-
我使用
fread函数将所有输入一次性读入。 -
注意读入数据大小。字符数组开到
6 \times 10^5 是够的(小一点我也没试过)。 -
如果你使用了
fread且从控制台输入,则可以换行输入 ctrl + z 作为结束标志。
读入字符串的处理:
-
从前往后的顺序遍历输入字符串。
-
由题目可知,所有 BBCode 标签都被中括号包裹,仅当一个字符为
[时,以这个字符为开头的某个子串需要进行转换。所以每次遇到[时判断标签类型。 -
我使用了函数
judgeLabel判断标签类型。(不要忘记先判断这个标签有没有/。)
//给标签设置枚举类
typedef enum {
H1, H2, B, I, URL, IMG, QUOTE
} Label;
//... ...
//判断标签。
//static inline 是优化用的
static inline Label judgeLabel() {
if (*cursor == 'h') {
cursor++;
if (*cursor == '1') {
cursor += 2; //跳过后括号
return H1;
} else {
cursor += 2;
return H2;
}
} else if (*cursor == 'b') {
cursor += 2;
return B;
} else if (*cursor == 'i') {
if (*(cursor + 1) == 'm') {
cursor += 4;
return IMG;
} else {
cursor += 2;
return I;
}
} else if (*cursor == 'u') {
cursor += 4;
return URL;
}
cursor += 6;
return QUOTE;
}
-
使用一个栈存储暂未闭合的标签,用来检查语法错误。
-
在判断完标签类型后将对应标签压入 / 弹出标签栈中,并检查语法错误。
bool is_end = false; //存储这个标签是否是结束标志(是否有斜杠)
if (*cursor == '/') {
cursor++;
is_end = true;
}
Label now_label = judgeLabel();
if (!is_end) {
labPush(now_label); //labPush、labPop 等为栈操作。函数就一行。具体见源代码。
} else {
if (labEmpty()) {
puts("Unclosed Mark");
return 0;
}
if (labTop() != now_label) {
puts("Match Error");
return 0;
}
labPop();
}
之后处理各个标签。
-
注意转换后的结果不能直接输出,万一后面有个得报错的就寄了。所以我开了一个数组作为输出缓冲区。
ansAppend函数的功能是在输出缓冲区的末尾追加一个字符。 -
语法错误之前已经判断过了,所以这里不用(也不能)重复判断。
-
h1、h2、b、i标签直接换成对应的字符就可以了。注意空格位置。 -
url、img标签只差了一个感叹号,所以可以一起处理。 -
url、img标签中两段文本 BBCode 和 Markdown 顺序是反的(BBCode:[url=链接文本]描述文本[/url])。因为描述文本中可能还会有其它标签需要转换,不能也一股脑读入,所以读到[url=时,先将链接文本读入,并放入一个 字符串栈 中。 -
等读到
[/url]时再从字符串栈中取栈顶并弹出,转换为相应 Markdown。(以上两条img同理)。 -
最后是
quote。这个标签比较烦的就是开始和结束的换行问题。[quote]和[/quote]两个标签的前后四个位置都可能有换行,但是我们只需要开始一个和结束一个的换行,且结束的换行后不应加上>。 -
遇到
quote时,将其包裹的字符全部读入,直到遇见完整的[/quote]。其中如遇换行则加上>,并特殊判断一下开始和结束的换行。
//... ...
else if (now_label == QUOTE) {
if (*cursor == '\n' && (*(ans_cursor - 1) == '\n' || ans_cursor == ans))
cursor++; //防止多换行:
//如果输出缓冲区的最后一个字符是换行而且紧接着[quote]光标读到的
//也是一个换行,那么就让光标后移,跳过换行。
if (*(cursor - 1) != '\n') { //判断 [quote] 前面是否没有换行
//解决无换行quote第一行没有> 的问题
if (*(ans_cursor - 1) != '\n')
ansAppend('\n'); //如果 [quote] 两边都没有换行就加上一个换行。
ansAppend('>');
ansAppend(' ');
}
do {
if (*cursor == '\n') {
ansAppend('\n'); //解决最后一行换行多出> 的问题。
cursor++; //如果这个字符是换行,先把换行输出,之后去看看后面紧接着的是不是 [/quote]
//如果是就会在后面判断时 break 掉,不会输出 >
}
if (*cursor == '[' && *(cursor + 1) == '/' && *(cursor + 2) == 'q' &&
*(cursor + 3) == 'u' && *(cursor + 4) == 'o' && *(cursor + 5) == 't' &&
*(cursor + 6) == 'e' && *(cursor + 7) == ']')
break;
if (*(cursor - 1) == '\n') {
ansAppend('>');
ansAppend(' ');
}
ansAppend(*cursor);
} while (*(cursor++) != '\0');
if (*cursor == '\0') {
//整个输入读完了之后都没找到 [/quote]
puts("Unclosed Mark");
return 0;
}
cursor += 8; //跳过标签
if (*cursor == '\n' && *(ans_cursor - 1) == '\n')
cursor++; //防止多换行
labPop(); //弹出栈中的quote
}
最后一点
海内存知己,天涯若比邻。请稍等...
-
不要忘了判断标签栈是否空了,如果没空记得报错。
-
将输出缓冲区中的内容全部输出。
-
等待 AC。
祝你们成功(滑稽