6
\$\开始组\$

我想创建一个系统,用插入到流中的标记标记文本。每个标记构成文本的一部分,创建一个树状结构,就像经典标记语言(HTML、XML…)一样。我想根据标记外部化文本的格式。我找到了一种可行的方法,但由于我不习惯标准::流在C++中,我希望能为这个项目提供建议和代码审查。

#包括<iostream>#包括<sstream>#包含<矢量>使用命名空间标准;enum类标记{无标记,第页,跨度,它,相对长度单位};结构部件{部件(标记标记=标记::no_Tag,常量标准::string&s=“”):标记(标记),s(s){}标签;std::字符串s;};结构FormatterBase{虚拟字符串格式(const Tag Tag,const string&s)const=0;虚拟~FormatterBase(){}};struct Formatter:公共FormatterBase{字符串格式(标记标记,常量字符串)常量覆盖{开关(标签){case标签::no_Tag://标准::cout<<“format no_tag”<<s<<“\n”;返回s;case标签::p://std::cout<<“format p”<<s<<“\n”;返回s+“\n”;case标签::span://标准::cout<<“format span”<<s<<“\n”;return“<”+s+“>”;case标签::strong://标准::cout<<“format it”<<s<<“\n”;return“*”+s+“*”;case标签::em://标准::cout<<“format em”<<s<<“\n”;return“**”+s+“**”;}}};struct MarkupStream:公共ostream{MarkupStream(常量格式设置工具&fm,标记标记=标记::no_Tag):_fm(调频),_parts({{标记,“}}){}模板<T类>标记流写入(&T){串流;s<<文本;_parts.back().s+=s.str();返回*this;}标记流连接(&C){_parts.back().s+=流.release_last_part();返回*this;}MarkupStream和operator()(MarkupStream&stream){返回concat(流);}MarkupStream运算符()(标记标记)(&O){_parts.template_back(标记,“”);返回*this;}字符串release_last_part(){auto&last=_parts.back();auto res=_fm.format(last.tag,last.s);_parts.pop_back();返回res;}常量格式设置工具&_fm;向量<Part>_parts;};模板<T类>MarkupStream和operator<<(MarkupStream&stream,T文本){return-stream.write(文本);}MarkupStream和operator<<(MarkupStream&lf,MarkupStreame&rg){返回lf.concat(rg);}int main(){格式化程序格式;标记流(fm);s(标签::p)<<“Hello World!”<<(标签:;cout<<s.release_last_part();返回0;}

以下是此代码的输出:

你好,世界<12>你好!

\$\端组\$

2个答案2

重置为默认值
6
\$\开始组\$

关于您的API

要实际使用代码,您必须编写相当多的代码行才能打印出一行。考虑您的main()功能:

格式化程序格式;标记流(fm);s(标签::p)<<“Hello World!”<<(标签:;cout<<s.发布_最后_部分();

在这里你需要了解格式化程序,标记流和几个标签::*s、 在构建格式化流之后,您必须调用发布_最后_部分()获取格式化字符串。考虑一下如何使用标准::format():

标准::cout<<std::format(“Hello World!<*{}{}*>**{}**\n”,1,2,“hi!”);

当然,这对段落、跨距、斜体和强调的格式进行了硬编码。理想情况下,你想要的东西与标准::格式,同时可以选择自定义格式化程序。例如,您可以创建自己的函数,该函数可以解析格式字符串并将渲染委托给另一个函数:

模板<类型名。。。Args>标准::字符串format_markup(FormatterBase&formatter,标准::string_view format_string,参数&…参数){}整型main(){格式化程序格式{};标准::cout<<format_markup(fm,“Hello World!<*{}{}*>**{}**\n”,1,2,“hi!”);}

当然,您必须想出自己的格式字符串语法。你可以重复使用<,***,这样就有了类似markdown的语法。但你可以创建一个HTML格式设置工具然后将其转换为HTML标记。

您还可以定义自定义格式化程序对于标准::格式,但自标准::格式不处理要格式化的东西的嵌套,我认为这不是一种方法。

不要忘记转义包含标记的字符串

考虑写下:

s(标记::p)<<“表达式的结果”<<(s(标签::strong)<<“3*3”)<<“是9。”;

因为有一个*在您想要增强的文本中,输出将被错误地呈现。因此,您需要在试图标记的字符串中查找标记,并以某种方式转义这些标记字符。

标记流()应该引用格式设置基数

的第一个参数标记流()格式化程序&,但这会阻止您创建另一个超负荷格式设置基数并使用它。确保第一个参数的类型为格式设置基数&.

效率不高

许多内存分配和字符串拷贝都是由您的代码完成的。如果您偶尔渲染标记的文本,这很好,但如果您经常使用它,可能会成为性能瓶颈。理想情况下,您尽可能避免存储数据,但由于嵌套,您仍必须存储中间结果。不过,还是试着标准::移动字符串。您还可以避免标准::矢量<零件>:考虑到你只打电话pop_back()返回格式化的标准::字符串.

你不必使用操作员<<

虽然我们知道操作员<<C++基于流的IO功能,我不确定它们是否适合您正在尝试的操作。它需要您编写大量括号,使使用它的代码更像LISP而不是C++。如果你做了标记流::operator()接受更多参数?考虑能够写:

使用enum Tag;std::cout<<s(p,“Hello World!”,s(span,s(it,1,2)),“”,s,(em,“hi!”);

所以运算符()将标记作为第一个参数,然后是任意数量的字符串,在使用格式化程序格式化结果之前,它将连接这些字符串,然后将格式化结果作为标准::字符串.

考虑进一步分离标记和格式

我在代码中看到的一个问题是s(标记::foo)它将标记标记与进行格式化的流相结合。如果您可以先构建一个标记字符串,然后将其传递给进行实际格式化的对象,该怎么办?你的代码可能看起来像:

auto-marked_up_text=P(“你好,世界!”,Span(It(1,2)),“”,Em(“嗨!”));格式化程序格式{};标准::cout<<格式标记(fm,标记up_text);

在哪里?P(P),跨度,相对长度单位现在是存储标记及其参数列表的类,允许格式_标记()要递归遍历列表,请使用传入的格式设置基数.

\$\端组\$
1
  • \$\开始组\$ 感谢您的相关反馈,我想我会选择逗号符号,它非常漂亮。 \$\端组\$ 评论 5月24日7:29
4
\$\开始组\$

避免使用命名空间标准;

标准命名空间是一个相当大的命名空间。这种行为完全消除了名称空间的好处,因为它将其所有名称都引入了全局名称空间。请检查“using namespace std;”有什么问题?并养成使用(非常短的)名称空间前缀的习惯标准::.

\$\端组\$

你的答案

单击“发布您的答案”,表示您同意我们的服务条款并确认您已阅读我们的隐私政策.

不是你想要的答案吗?浏览已标记的其他问题问你自己的问题.