读取多行但特别是......有效地解析它们

时间:2011-10-16 12:20:30

标签: c++ parsing split cin

我需要在开头阅读具有特定关键字的多行。 我有一个基本问题,我需要帮助我。

以下是输入类型:

  

keyword1 0.0 0.0
  keyword1 1.0 5.0
  keyword2 10.0
  keyword3 0.5
  keyword4 6.0

规则是:

  • 包含keyword1&的行keyword2应该按照该顺序和任何其他行之前。

  • 包含keyword3&的行keyword4可以按任何顺序

  • keyword1后面跟着2个双

  • keyword2,3& 4必须遵循1双

  • 在包含所有四个关键字后跟其双精度的行块的末尾,“循环”中断并触发计算。

这是我的来源:

using namespace std;

int main (int argc, const char * argv[]) {    
    vector<double> arrayInputs;
    string line;
    double keyword1_first, keyword1_second, keyword4, 
          keyword3, keyword2;
    bool inside_keyword1=false, after_keyword2=false, 
          keyword4_defined=false, keyword3_defined=false ;

//cin.ignore();

 while (getline(cin, line)) {
     if (inside_keyword1 && after_keyword2 && keyword3 && keyword4) {
         break;
     }
     else
     {
         std::istringstream split(line);
         std::vector<std::string> tokens;
         char split_char = ' ';
         for (std::string each; std::getline(split, each, split_char); tokens.push_back(each));

         if (tokens.size() > 2)
         {
             if (tokens[0] != "keyword1") return EXIT_FAILURE; // input format error
             else
             {
                 keyword1_first = atof(tokens[1].c_str());
                 keyword1_second = atof(tokens[2].c_str());

                 inside_keyword1 = true;
             }
         }
         else
         {
             if (tokens[0] == "keyword2")
             {
                 if (inside_keyword1)
                 {
                     keyword2 = atof(tokens[1].c_str());
                     after_keyword2 = true;
                 }

                 else return EXIT_FAILURE; // cannot define anything else keyword2 after keyword1 definition

             }
             else if (tokens[0] == "keyword3")
             {
                 if (inside_keyword1 && after_keyword2)
                 {
                     keyword3 = atof(tokens[1].c_str());
                     keyword3_defined  = true;
                 }
                 else return EXIT_FAILURE; // cannot define keyword3 outside a keyword1
             }
             else if (tokens[0] == "keyword4")
             {
                 if (inside_keyword1 && after_keyword2)
                 {
                     keyword4 = atof(tokens[1].c_str());
                     keyword4_defined  = true;
                 }
                 else return EXIT_FAILURE; // cannot define keyword4 outside a keyword1
             }
         }
     }
 }

 // Calculation


 // output


 return EXIT_SUCCESS;
}

我的问题是:除了在阅读/解析循环中使用布尔值之外,还有更有效的方法吗?

1 个答案:

答案 0 :(得分:2)

你问一些“更有效”的东西,但似乎你没有特定的表现目标。所以你想要的更像是Code Review。有一个网站,特别是:

https://codereview.stackexchange.com/

但无论如何......

你是正确的,因为这里并没有真正要求四个布尔人。这是2 ^ 4 = 16种不同的“状态”,其中许多是你永远无法达到的。 (例如,keyword3_defined == true时,您的规范明确禁止after_keyword1 == false

程序状态可以保存在枚举和布尔中。这使得“健忘”循环可以在不同情况下重新访问一行代码,但仍然记住它处理的处理阶段。它在许多情况下都很有用,包括在复杂的解析器中。但是如果你的任务是线性的和简单的,那么最好根据已达到某一行代码来隐式“知道”状态。

作为一个展示我正在谈论的对比的教育例子,这里是一个愚蠢的状态机,用一个字母A读后跟任意数量的字母B

enum State {
    beforeReadingAnA,
    haveReadAnA,
    readingSomeBs,
    doneReadingSomeBs
};

State s = beforeReadingAnA;
char c;
while(true) {
    switch (s) {
        case beforeReadingAnA: 
            cin >> c;
            if (cin.good() && c == 'A') {
                // good!  accept and state transition to start reading Bs...
                s = haveReadAnA;
            } else {
                // ERROR: expected an A
                return EXIT_CODE_FAILURE;
            };
            break;

         case haveReadAnA:
            // We've read an A, so state transition into reading Bs
            s = readingSomeBs;
            break;

         case readingSomeBs:
            cin >> c;
            if (cin.good() && c == 'B') {
                // good!  stay in the readingSomeBs state
            } else if (cin.eof()) {
                // reached the end of the input after 0 or more Bs
                s = doneReadingSomeBs;
            } else {
                // ERROR: expected a B or the EOF
                return EXIT_CODE_FAILURE;
            }
            break;

         case doneReadingSomeBs:
             // all done! 
             return EXIT_CODE_SUCCESS;
    }
}

如前所述,它是一种非常非常有用的编码样式。然而对于这种情况来说这很荒谬。与执行相同操作的简单线性代码进行比较:

// beforeReadingAnA is IMPLICIT

char c;
cin >> c;
if (cin.fail() || c != 'A')
   return EXIT_CODE_FAILURE;

// haveReadAnA is IMPLICIT

do {
    // readingSomeBs is IMPLICIT

    cin >> c;
    if (cin.eof())
       return EXIT_CODE_SUCCESS;
    if (cin.fail() || c != 'B')
       return EXIT_CODE_FAILURE;
}

// doneReadingSomeBs is IMPLICIT

所有状态变量都消失了。它们是不必要的,因为程序只是“知道它在哪里”。如果你重新考虑你的例子,那么你可以做同样的事情。你不需要四个布尔值,因为你可以将光标放在一行代码上,并放心地说这四个布尔值必须,如果那行代码恰好在运行。

就效率而言,<iostream>类可以让生活更轻松,而不是像atof那样调用C-isms或者不得不使用c_str()更加惯用C ++ }。让我们看一下代码的简化摘录,它只读取与“keyword1”相关的双精度。

string line;
getline(cin, line);
istringstream split(line);
vector<string> tokens;
char split_char = ' ';
string each;
while (getline(split, each, split_char)) {
    tokens.push_back(each);
}
double keyword1_first, keyword1_second;
if (tokens.size() > 2) {
    if (tokens[0] != "keyword1") {
        return EXIT_FAILURE; // input format error
    } else {
        keyword1_first = atof(tokens[1].c_str());
        keyword1_second = atof(tokens[2].c_str());
    }
}

与此对比:

string keyword;
cin >> keyword;
if (keyword != "keyword1") {
    return EXIT_FAILURE;
}
double keyword1_first, keyword1_second;
cin >> keyword1_first >> keyword1_second;

万。 Iostream可以检测您尝试读取或写入的类型。如果遇到以您要求的方式解释输入的问题,那么它会将输入留在缓冲区中,以便您可以尝试以另一种方式读取它。 (在要求字符串的情况下,行为是读取一系列字符直到空白......如果你真的想要整行,你可以像使用getline一样。)

然而,错误处理是您必须处理的事情。有可能告诉iostreams使用异常处理方法,以便遇到问题的标准响应(例如在预期有双重的地方的随机单词)将导致程序崩溃。但默认设置是您需要测试的失败标志:

cin erratic behaviour

iostream有细微之处,所以你可能想对问答进行一些调查......我最近在回答/问这里时已经学到了一点:

Output error when input isn't a number. C++

When to use printf/scanf vs cout/cin?