【问题标题】:read multiple lines but especially... parsing them efficiently阅读多行,但尤其是...有效地解析它们
【发布时间】:2011-10-16 12:20:29
【问题描述】:

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

下面是输入的种类:

关键字1 0.0 0.0
关键字1 1.0 5.0
关键字2 10.0
关键字3 0.5
关键字4 6.0

规则是:

  • 包含关键字 1 和关键字 2 的行应按此顺序排列,并且在任何其他行之前。

  • 包含关键字 3 和关键字 4 的行可以是任意顺序

  • keyword1 必须后跟 2 个双精度字

  • 关键字 2、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;
}

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

【问题讨论】:

  • 你定义了你的“问题”(就像你布置了一个任务或任务一样),你提供了来源,但你还没有向读者指出一个问题 为我们。你有什么问题?
  • 非常抱歉。问题是:是否存在另一种更有效的方法,而不是在读取/解析循环中使用布尔值?

标签: c++ parsing split cin


【解决方案1】:

您询问“更高效”的问题,但您似乎没有特定的性能目标。所以你在这里想要的可能更像是代码审查。有一个网站,特别是:

https://codereview.stackexchange.com/

但无论如何……

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

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

作为一个展示我所说的对比的教育示例,这是一个愚蠢的状态机,可以读取一个字母 A 后跟任意数量的字母 Bs:

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

所有状态变量都消失了。它们是不必要的,因为程序只是“知道它在哪里”。如果您重新考虑您的示例,那么您可能也可以这样做。您将不需要四个布尔值,因为您可以将光标放在一行代码上,并自信地说如果该行代码恰好正在运行,那么这四个布尔值将是必须是什么。 p>

就效率而言,&lt;iostream&gt; 类可以让生活变得比这里更轻松,并且更符合 C++ 的习惯,而无需调用像 atof 这样的 C-isms 或永远不必使用 c_str()。让我们看一下您的代码的简化摘录,它只读取与“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;

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

但是,错误处理是您必须处理的事情。可以告诉 iostreams 使用异常处理方法,这样遇到问题的标准响应(例如在预期为双精度的地方出现随机词)将使您的程序崩溃。但默认是设置一个你需要测试的失败标志:

cin erratic behaviour

iostream 存在细微差别,因此您可能想对问答进行一些调查...我最近在此处回答/提问时自己学习了一点:

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

When to use printf/scanf vs cout/cin?

【讨论】:

  • 非常感谢你,HostileFork。我对我问的方式等感到非常抱歉。我在这里学习如何使用/分享/讨论......关于这些概念,我非常感谢你。明天我会尝试在那段代码中实现 enum 和 iostream。我会告诉你。最好的问候,J.
  • 当然...很高兴帮助那些寻求改进他们的代码和方法的人。 (很多人只是懒得问。)但很快你就有机会去向其他人解释这些东西了! :) 请记住,我的部分观点与方法的对比是您可能不需要此特定程序的枚举。试着让你的程序“知道它在哪里”,并且只在需要时才引入状态。
  • 全部实现。不是最好的方式,但它应该工作。现在我有一个 seg fault 11 .. 跟踪它。我不知道我在哪里没有正确分配内存。
  • 只有在被证明是正确的时候才会实现! :-P 要获得灵感,请查看 Don Knuth 关于版本编号等的哲学:en.wikipedia.org/wiki/Donald_Knuth#Humor
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-13
  • 1970-01-01
  • 2016-09-15
  • 1970-01-01
相关资源
最近更新 更多