C++程序检查标记的有效性

C++ program checking for tag validity

本文关键字:有效性 程序 检查 C++      更新时间:2023-10-16

我是C++的初学者,曾试图让检查按预期进行。但几乎每次测试都是如此。在这种情况下,只假设返回true:

<red> Red blank <dim> I'm now dim and red. </dim> </red>

但现在也是这样:

<red> Blah I'm red.<dim> Im dim now </red> </dim>

或者这个:

<red> blah <im dim now

所以我想知道我的代码中是否有遗漏的地方。

bool is_well_formed(ifstream& ifs, string& error_msg) {
string fname,line;
Token tok;
Lexer lexer;
tags.insert("blue");
tags.insert("red");
tags.insert("cyan");
tags.insert("white");
tags.insert("yellow");
tags.insert("magenta");
tags.insert("dim");
tags.insert("underline");
tags.insert("bold");
stack<string> tagstack;
while (getline(ifs, fname)) { 
// tries to open the file whose name is in string fname
if (ifs.fail()) {
cerr << "ERROR: Failed to open file " << fname << endl;
ifs.clear();
} else {
while (getline(ifs, line)) {
lexer.set_input(line);
while (lexer.has_more_token()) {
tok = lexer.next_token();
string tmpTok = tok.value;
switch (tok.type) {
case TAG:
// If it has /, remove / from tmpTok
if (tok.value[0] == '/') {
// If it's a closing t
tmpTok = tmpTok.substr(1,tmpTok.length()-1);
}
if(tags.find(tmpTok) == tags.end()) {
// Check whether the encountered tag is valid
error_return("Tag " + tmpTok + " is invalid!");
return false;
} else {
// Valid Tag encountered
tagstack.push(tmpTok);
// Check if the tags are formed properly
if (tmpTok.find('/')) {
// Remove / from tmpTok
string closingTag =  tmpTok;
string openingTag = tagstack.top();
tagstack.pop();
if(closingTag.compare(openingTag) != 0) {
error_return(closingTag+"doesn't match" +openingTag);
return false;
} //else 
//  return true; // if the file is well formed
}/**else{
tagstack.push(tmpTok);
}*/
}// else end    
break;
case IDENT:
// cout << "IDENT: " << tok.value << endl;
break; 
case ERRTOK:
error_return("Syntax error on this linen");
return false;
//cout << "Syntax error on this linen";
break;
case ENDTOK:
break;
}
}
}
}
}
return true; // if the file is well-formed
}

对于如此简单的事情(这一定很简单,因为这不可能解析真实世界中知道你是什么的人-XML)有几件事可能会有所不同,但你的直接问题是:

stack<string> tagstack声明移动到每行循环之外,或者更好地移动到整个处理循环之外。一旦您将标记推到堆栈上并退出声明它的else{}块的作用域,它现在所在的位置就会丢失该状态。

您的状态机可以使用一些工作来提高健壮性,但这是另一个问题,请首先修复标记堆栈的范围。

你需要在调试器下运行它,你应该习惯这一点,因为我认识的每一位专业的C/C++工程师都在一个调试器中度过了一半的生产生命。它是随业务而来的。话虽如此,关于总是返回true,我将向您提供这些问题,不是在这里回答,而是在调试器中再次进行调查

  1. 如果lexer从不返回TAG的令牌类型,会发生什么
  2. 如果lexer返回TAG类型的令牌,但它从不以"/"开头,会发生什么
  3. 如果lexer返回一个标记类型的TAG,并且它以"/"开头,但您随后又剪掉了"/",会发生什么

在代码的一个地方,您似乎正在检查标记的第一个字符是否为"/",而不到两行之后,您似乎试图抛出开头和结尾元素标记"<"和'>'。具体而言:

if (tok.value[0] == '/') {
// If it's a closing t
tmpTok = tmpTok.substr(1,tmpTok.length()-1);
}

这两行代码对于结束标记来说并不一致。首先检查前导'/',表示<>已经被剥离,下一行您继续从字符串中剥离前导字符和尾字符,就好像同时具有一样<>仍然存在。但是,如果它们不存在(而且不可能存在,否则你对"/"的检查将是错误的。你现在看到了吗?你正在切割"/"。这很重要吗?好吧。进一步查看代码,我们会发现:

if (tmpTok.find('/')) {
// Remove / from tmpTok
string closingTag =  tmpTok;
string openingTag = tagstack.top();
tagstack.pop();
if(closingTag.compare(openingTag) != 0) {
error_return(closingTag+"doesn't match" +openingTag);
return false;
} //else
}

由于您刚刚切断了'/'的检查,因此不会执行其中的任何代码。因此,您将在整个文件中一个接一个地添加令牌,以为它们都是入口令牌,然后用完行,然后返回true。事实上如果文件末尾的标签堆栈上有任何内容,那么一定是平衡不平衡,因此会出现错误。把那个条件检查作为你整体评估的一部分,我保证你会开始看到错误的过滤。

现在让您使用调试器。我只是通过仔细阅读代码才发现了这一切。想象一下,当在调试器中逐行运行时,您会看到什么,在调试器中您可以实时看到更改。