这并不多,但我尝试使用了一些我学到但从未真正使用过的东西,因为在我工作的地方(大部分情况下)并不真正需要这种代码。我试着尽可能多地做C++11。我想知道我是否在某个地方搞砸了,也许有些事情可以做得更好,或者你有一些提高可读性的建议?
#include <string>
#include <regex>
#include <vector>
class JSONPretify : public std::string{
public:
JSONPretify(std::string j){
this->assign(j);
pretify();
};
JSONPretify(std::string j, bool colon_space){
this->assign(j);
pretify();
if(colon_space)
insertColonSpaces();
};
private:
void pretify(){
std::regex var = std::regex(R"((\".+?\".*?(?=\{|\[|\,|\]|\}))|(\d+?))");
long it = 0;
int depth = 0;
while(it < this->size() && it != -1){
regex_pos pos_tab = findRegexFirstPosition(it, var);
long pos_comma = this->find(",", it);
long pos_obj_start = this->find("{", it);
long pos_obj_end = this->find("}", it);
long pos_array_start = this->find("[", it);
long pos_array_end = this->find("]", it);
long old_it = it;
unsigned long work_with = find_lowest(std::vector<long>{pos_tab.pos, pos_comma, pos_obj_start, pos_obj_end,pos_array_start,pos_array_end});
switch(work_with){
case(TAB):{
std::string insert = generateSpaces(depth);
this->insert(pos_tab.pos, insert);
it = pos_tab.pos+insert.size()+pos_tab.length;
break;
}
case(COMMA):{
std::string insert = "\n";
this->insert(pos_comma+1, insert);
it = pos_comma+1;
break;
}
case(OBJ_START):{
std::string insert = "\n";
this->insert(pos_obj_start+1, insert);
it = pos_obj_start+insert.size();
depth+=1;
if(pos_obj_start-1 < 0 || pos_obj_start > this->size()) continue;
if(this->at(pos_obj_start-1) != ':'){
std::string extra = generateSpaces(depth-1);
this->insert(pos_obj_start, extra);
it+=extra.size();
}
break;
}
case(OBJ_END):{
std::string insert = "\n"+generateSpaces(depth-1);
this->insert(pos_obj_end, insert);
depth-=1;
it = pos_obj_end+insert.size()+1;
break;
}
case(ARRAY_START):{
depth+=1;
std::string insert = "\n";
this->insert(pos_array_start+1,insert);
it=pos_array_start+insert.size();
break;
}
case(ARRAY_END):{
depth-=1;
std::string insert = "\n"+generateSpaces(depth);
this->insert(pos_array_end,insert);
it=pos_array_end+insert.size()+1;
break;
}
default:{
break;
}
};
if(it == old_it)
break;
}
};
void insertColonSpaces(){
long pos = 0;
while(pos < this->size() && pos != -1){
pos = this->find(":", pos);
if(pos == -1 || pos >= this->size()) break;
this->replace(pos,1, " : ");
pos+=3;
}
}
struct regex_pos{
long pos;
long length;
};
std::string generateSpaces(int l){
std::string r="";
for(int i = 0; i < l; i++){
r+= " ";
}
return r;
}
regex_pos findRegexFirstPosition(long start_pos, std::regex rx){
long at = -1;
long l = 0;
std::string ss(this->begin()+start_pos, this->end());
std::smatch m;
std::regex_search ( ss, m, rx );
for (unsigned i=0; i<m.size(); ++i) {
at = m.position(i);
l = m[i].str().size();
break;
}
if(at != -1) at += start_pos;
return {at,l};
}
template<typename T>
unsigned long find_lowest(std::vector<T> outof){
unsigned long lowest_it = 0;
for(unsigned i = 0; i < outof.size(); i++){
if((outof[i] < outof[lowest_it] && outof[i] != -1) || (outof[lowest_it] == -1 && outof[i] != -1)){
lowest_it = i;
}
}
if(outof[lowest_it] == -1)
lowest_it = outof.size()+1;
return lowest_it;
}
enum positions{
TAB = 0,
COMMA = 1,
OBJ_START = 2,
OBJ_END = 3,
ARRAY_START = 4,
ARRAY_END = 5
};
};发布于 2016-06-22 22:03:25
谢谢你编辑这个问题。我几乎没有什么想法,你可能想要完善你的代码。
JSONPretify来源于std::string,这引起了我的注意。一般说来,STL容器并不适用于这种用例。特别是,如果您是从基类派生来检查它是否有虚拟析构函数,这是一个好主意。std::string没有virtual析构函数。
我将进一步建议,由于您不需要建模任何状态,保留任何数据或不变的简单函数可能是更好的接口。
std::string pretify(const std::string& j, bool colon_space = false);实现
为了能够向代码的用户隐藏所有复杂的细节,您可以将其划分为接口和实现。最常见的形式是仅迭代头文件(例如prettify.hpp)和实现源文件(例如,prettify.cpp)。然后,您可以保留prettify.cpp的所有定义和实现细节。要将其与代码的其余部分(即使是仅链接到的代码)分离开来,您可以使用匿名名称空间或内部链接函数(令人惊讶的是,这是关键字static的其他含义)。
我会尽量避免这个算法的实现,并使用std::string::find_first_of()和/或std::min_element()。
如果您决定坚持使用它,那么您仍然可以简化它,不让它成为一个模板,因为它只有一个调用。您可能也不希望复制参数向量,因此引用可能更合适:
unsigned long find_lowest(const std::vector<long>& outof){我建议使用作用域枚举(伟大的C++11扩展),并在这些枚举和整数之间进行区分。
见http://en.cppreference.com/w/cpp/language/enum 作用域枚举
它所需要的只是新的positions定义:
enum class positions{并更改值用法:
case(positions::TAB):{我对此感到困惑
positions work_with = find_lowest(std::vector<long>{pos_tab.pos, pos_comma, pos_obj_start, pos_obj_end,pos_array_start,pos_array_end});因为您将位置分配给work_with,但在switch中检查内容。这是正确的吗?
这是一种主观观点,但省略一些辅助变量可能会增加可读性。
case(COMMA):{
std::string insert = "\n";
this->insert(pos_comma+1, insert);
it = pos_comma+1;
break;
}缩短为
case(COMMA):{
this->insert(pos_comma+1, "\n");
it = pos_comma+1;
break;
}对于您创建的并且不打算更改的变量,我肯定会使用const让编译器知道您的意图,并让它实际检查您是否意外地违反了它。
const std::regex var = std::regex(R"((\".+?\".*?(?=\{|\[|\,|\]|\}))|(\d+?))");
const regex_pos pos_tab = findRegexFirstPosition(it, var);
const std::string insert = generateSpaces(depth);基本上,您正在用另一个字符串替换一个字符串。
这个问题可能会给出一些提示(例如boost::algorithm::replace_all_copy或使用std::regex)。https://stackoverflow.com/questions/5343190/how-do-i-replace-all-instances-of-a-string-with-another-string
除非我忽略了一些东西,否则它和
return std::string(l * 4, ' ');在这里检查std::string“填充”构造函数:http://www.cplusplus.com/reference/string/string/string/
这个回路
for (unsigned i=0; i<m.size(); ++i) {
at = m.position(i);
l = m[i].str().size();
break;
}看起来更像是一个简单的条件
if ( m.size() > 0 ) {
at = m.position(0);
l = m[0].str().size();
}发布于 2016-06-23 04:03:31
至少在理论上,这里使用的继承有点危险--可以隐式地将JSONPretify转换为std::string,更重要的是隐式地将JSONPretify *转换为std::string *。如果用户最终通过JSONPretify删除std::string *,则会得到未定义的行为。
因为std::string没有任何虚拟函数,所以没有覆盖任何虚拟函数。这意味着您不会通过使用继承获得更多的收益。如果您真的想使用继承,我至少会考虑使用私有继承。这样可以防止隐式转换到基类,从而消除(大部分)危险。用户仍然可以使用显式强制转换到基类,但至少是强迫他们显式地进行转换。
this->虽然有些语言要求您使用this-> (或类似的语言,如self)来引用成员变量/函数,但是C++并不这样做--这样做会增加大量的视觉噪音,从而分散和损害可读性。除非真的有必要,否则它应该被避免(而这不是这里)。
这直接导致查看一些变量名,如insert。选择好的名称通常很困难,但名称并不短缺,因此我们需要为函数和变量都使用相同名称的单个语句,例如:this->insert(pos_obj_start+1, insert);。我会将变量重命名为类似于leader或prolog的内容。
例如,您有两个几乎相同的构造函数:
JSONPretify(std::string j){
this->assign(j);
pretify();
};
JSONPretify(std::string j, bool colon_space){
this->assign(j);
pretify();
if(colon_space)
insertColonSpaces();
};我至少会考虑把这些简化成一个默认的参数:
JSONPretify(std::string j, bool colon_space = false){
this->assign(j);
pretify();
if(colon_space)
insertColonSpaces();
};这通常会保留相同的行为--您可以将其调用为:
JSONPretify(input);
JSONPretify(input, false); // same as above
JSONPretify(input, true);虽然这可能不是什么大不了的事,但我猜您通常不希望允许从std::string到JSONPretify的隐式转换,所以您最好将只能用一个参数调用的构造函数标记为explicit。
explicit JSONPretify(std::string j, bool colon_space = false) {
this->assign(j);
pretify();
if(colon_space)
insertColonSpaces();
};当查看客户端代码时,我怀疑JSONPretify(foo, true)与JSONPretify(foo, false)有何不同(也就是说,布尔值控制的内容并不明显)。我会考虑使用enum来代替:
enum class colons { tight, spaced };
explicit JSONPretify(std::string j, colons spacing=colons::tight) {
this->assign(j);
pretify();
if(spacing == spaced)
insertColonSpaces();
};这将被引用,类似于:
JSONPretify(foo, colons::spaced);或者:
JSONPretify(foo, colons::tight);...which看上去更清晰一些,至少对我来说是这样(尽管有些人可能更喜欢tight以外的名字)。
我甚至不愿提及这一点,但似乎这个类名应该有两个"t"s:JSONPrettify。
发布于 2016-06-23 13:31:38
Nit:次要的,但函数定义不需要";“后面的"}”
pretify();
};而不是开关语句作用域!
https://codereview.stackexchange.com/questions/132790
复制相似问题