LG2395 BBCode转换Markdown

发布于 2020-12-21  3.4k 次阅读


题面

题意

给出一段 BBCode 格式的代码,让你将其转换为 Markdown 格式。

具体语法展示在题面中,其中减少了相当多的语法,并且对输入的代码作出了一些大大减轻码量的限制。

分析

如果是让你将一个没有语法问题的 BBCode 代码转换成 Markdown 代码,相信一定不难。对于除 quote 以外的指令,你可以使用直接替换的方式转换代码,如 [h1] 转换为 #, [/b] 转换为 __。而 quote 则相对难处理,因为 quote 块内可能出现其他的标识符,从而影响你的解析。事实上你只需要找到 [quote] 出现后的第一个 [/quote],然后将他们中间的部分拿出来直接作为 quote 块即可。

那么我们考虑可能存在语法错误的情况。

  1. 标签匹配错误:解决标签匹配错误最常规的方法,就是开一个指令栈。每当遇到一个起始标签,就将它压入指令栈;遇到一个结束标签,就检查指令栈的栈顶是否与它对应。如果不对应,则说明出现了 Match Error 情况。
  2. 标签未闭合:在上文指令栈的基础上,只需要在处理完整个输入之后判断一个指令栈是否不为空。如果不为空,则说明还有些标签是失配的,出现了 Unclosed Mark 情况。

毕竟是大模拟,细节处还需参见代码。

代码

/**
 * @file P2395.cpp
 * @author Macesuted
 * @date 2020-12-21
 *
 * @copyright Copyright (c) 2020
 *
 */

#include <bits/stdc++.h>
using namespace std;

//需压入指令栈的操作的对应值
#define val_h1  1
#define val_h2  2
#define val_i   3
#define val_b   4
#define val_url 5
#define val_img 6

#define bufferSiz 1 << 16
char B[bufferSiz], *_S = B, *_T = B;
#define getchar() (_S == _T && (_T = (_S = B) + fread(B, 1, bufferSiz, stdin), _S == _T) ? EOF : *_S++)

string output;//输出缓存
stack<int> cmdStack;//指令栈
stack<string> infoStack;//URL信息临时存储栈

void checkStack(int val)
{
    if(cmdStack.empty()||cmdStack.top()!=val) puts("Match Error"),exit(0);//如果栈顶标签与此标签不同
    return cmdStack.pop();
}

int main() {
    char c=getchar();
    while(c!=EOF)
    {
        if(c=='[')
        {//开始读入标签
            string cmd="";
            c=getchar();
            while(c!=']') cmd.push_back(c),c=getchar();
            //此时已经读入完整的标签(在中括号中间的内容)
            bool reverse=false;//false表示起始标签,true表示结束标签
            if(cmd[0]=='/') reverse=true,cmd=cmd.substr(1);
            if(cmd=="h1")
            {//直接转换
                if(!reverse) output.append("# "),cmdStack.push(val_h1);
                else output.append(" #"),checkStack(val_h1);
            }else if(cmd=="h2")
            {//直接转换
                if(!reverse) output.append("## "),cmdStack.push(val_h2);
                else output.append(" ##"),checkStack(val_h2);
            }else if(cmd=="i")
            {//直接转换
                output.push_back('*');
                if(!reverse) cmdStack.push(val_i);
                else checkStack(val_i);
            }else if(cmd=="b")
            {//直接转换
                output.append("__");
                if(!reverse) cmdStack.push(val_b);
                else checkStack(val_b);
            }else if(cmd.substr(0,3)=="url")
            {
                if(!reverse) infoStack.push(cmd.substr(4)),//由于URL信息在Markdown中后置,所以开栈临时存储
                output.push_back('['),cmdStack.push(val_url);
                else output.append("]("+infoStack.top()+")"),infoStack.pop(),checkStack(val_url);
            }else if(cmd.substr(0,3)=="img")
            {//与上面唯一的区别就是'['的左侧多了一个'!'
                if(!reverse) infoStack.push(cmd.substr(4)),output.append("!["),cmdStack.push(val_img);
                else output.append("]("+infoStack.top()+")"),infoStack.pop(),checkStack(val_img);
            }else if(cmd=="quote")
            {//由于quote的特殊性,这里一次性读到quote的结束标签
                char c=getchar();
                while(c=='\n'||c=='\r') c=getchar();//删除quote块的头部空行
                string cache="";//cache临时存储读进来的所有内容
                while(c!=EOF)
                {
                    cache.push_back(c);
                    //如果cache字符串的末尾是"[/quote]",说明已结束
                    if(cache.size()>7&&cache.substr(cache.size()-8)=="[/quote]") break;
                    c=getchar();
                }
                if(c==EOF) { puts("Match Error"); return 0; }//如果读完了全文还没有读到结束标签
                //比较愚笨的删除方法
                cache.pop_back(),cache.pop_back(),cache.pop_back(),cache.pop_back(),
                cache.pop_back(),cache.pop_back(),cache.pop_back(),cache.pop_back();
                while(cache.back()=='\n'||cache.back()=='\r') cache.pop_back();//删除quote块的尾部空行
                if(output.back()!='\n'&&output.back()!='\r') output.push_back('\n');//quote块前需有空行
                output.append("> ");//以头部符号开始
                for(string::iterator i=cache.begin();i!=cache.end();i++)
                {
                    output.push_back(*i);
                    if(*i=='\n'||*i=='\r') output.append("> ");//每次换行都新增头部符号。
                }
                output.push_back('\n');//最后需要换行
            }
        }
        else output.push_back(c);//如果是常规字符,则应该输出
        c=getchar();
    }
    if(!cmdStack.empty()) { printf("Unclos"),puts("ed Mark"); return 0; }//如果指令栈没空
    printf("%s\n",output.c_str());//输出缓存的输出
    return 0;
}

我缓慢吐出一串啊吧啊吧并不再想说话