std::istringstream.good() 返回 true 的次数比预期多一次

med*_*ivh 1 c++ string while-loop

我正在制作一个玩具程序来从字符串创建类(例如,我向它提供“test1 test2”,它会生成 test1.cpp、test1.h、test2.cpp、test2.h)

\n\n

该操作发生在该函数中:

\n\n
bool makeClassesFromString(std::string classNames){\n\n    bool returnValue = true;\n\n    if (classNames == ""){\n\n        returnValue = false;\n\n    }\n    else{\n\n        std::istringstream issClassNames (classNames);\n        std::string sClassName;\n\n        while(issClassNames.good()){\n\n            issClassNames >> sClassName;\n            std::string sourceName = sClassName;\n            sourceName += ".cpp";\n\n            std::string headerName = sClassName;\n            headerName += ".h";\n\n            std::ofstream source(sourceName.c_str()), std::ios::app);\n            std::ofstream header(headerName.c_str()), std::ios::app);\n\n            //source << "#include \\"" << headerName << "\\"";\n\n\n            source.close();\n            header.close();\n\n        }\n\n    }\n\n    return returnValue;\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这些文件以追加模式打开,以确保任何已存在的类不会被意外覆盖。

\n\n

我最近返回到这个程序以包含一些额外的内容 - 特别是,旧版本只是创建了两个空文件,我想修改它以创建具有必要定义和包含的文件,这样我就不必手动进入并包含每次添加它们。这揭示了意想不到的行为 - 最后一个类将任何文本添加两次。

\n\n

在上面的代码示例中,我已经注释掉了我开始处理此问题的行,但是当我不注释掉它时,我会得到如下行为:

\n\n
input:\nclassmaker.exe test1 test2\n\noutput:\ntest1.h\ntest2.h\ntest1.cpp //(Which contains: #include "test1.h")\ntest2.cpp //(Which contains: #include "test2.h"#include "test2.h"\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的猜测是,我while(issClassNames.good())用最后一个类名返回了额外的时间(因此再次将字符串附加到源文件中),但我不确定是什么行为导致了这一点。

\n\n

目前提出的解决方案:

\n\n
    \n
  1. ifstream在以 模式打开之前,测试尝试以ofstream模式打开的文件是否已存在。(问题:没有解决运行while一次模式不必要的问题)
  2. \n
  3. 删除附加模式。(问题:有覆盖已有类的风险,仍然不能解决 while 循环问题)
  4. \n
  5. 在 while 循环中使用条件测试,不要在上次运行时打开文件,例如将当前类名与最后一个类名进行比较,如果相等则中止。问题:不知道如何检查这是否是上次运行的,除了笨拙的“这与上次的类名相同吗?” 如果输入字符串是“test1 test1 test2”,则测试也可能有过早中止的风险
  6. \n
\n\n

编辑

\n\n

我已经意识到: istringstream.good() 并不是真的比预期返回 true 一次,而是它的计算结果如下:

\n\n
input: "test1 test2"\n\nevaluation:\ngood = true; extract next to string //(succeeds, overwrites empty string with "test1")\ngood = true; extract next to string //(succeeds, overwrites "test1" with "test2")\ngood = true; extract next to string //(fails, Doesn\'t overwrite "test2")\ngood = false; break loop.\n
Run Code Online (Sandbox Code Playgroud)\n\n

编辑 2 + 3

\n\n

嗯,这很容易解决。谢谢你们大家。不幸的是,我只能将一个答案标记为已接受,因此我选择了第一个发布的答案,但我感谢您的帮助。

\n\n

最终的完整程序:

\n\n
#include <iostream>\n#include <fstream>\n#include <string>\n#include <sstream>\n\nvoid makeClassesFromStdInput();\nvoid makeClassesFromParameter(int argc, const char** argv);\nbool makeClassesFromString(std::string classNames);\n\nint main(int argc, const char** argv){\n\n    switch (argc) {\n\n        case 0:  //This shouldn\xe2\x80\x99t happen\n\n            break;\n\n        case 1:\n\n            //This will keep making classes from the CLI until\n            //the user breaks the process.\n            makeClassesFromStdInput();\n            break;\n\n        default:\n\n            //This will make classes from the parameters passed\n            //to the program except the first parameter which is\n            //assumed to be the program name.\n\n            makeClassesFromParameter(argc, argv);\n            break;\n\n    }\n\n    return 0;\n\n}\n\nvoid makeClassesFromStdInput(){\n\n    std::cout << "Input class name and press enter.\\nWhitespace delimits class names.\\nA blank line ends the process.\\n>";\n    bool running = true;\n    std::string classNames;\n\n    while(running){\n\n        std::getline(std::cin, classNames);\n        running = makeClassesFromString(classNames);\n\n    }\n\n}\n\nvoid makeClassesFromParameter(int argc, const char** argv){\n\n    std::string classNames = "";\n\n    for(int i = 1; i<argc; i++){\n\n        classNames += argv[i];\n        classNames += " ";\n\n    }\n\n    makeClassesFromString(classNames);\n\n}\n\nbool makeClassesFromString(std::string classNames){\n\n    bool returnValue = true;\n\n    if (classNames == ""){\n\n        returnValue = false;\n\n    }\n    else{\n\n        std::istringstream issClassNames (classNames);\n        std::string sClassName;\n\n        while(issClassNames >> sClassName){\n\n            std::string sourceName = sClassName;\n            sourceName += ".cpp";\n\n            std::string headerName = sClassName;\n            headerName += ".h";\n\n            std::ofstream source(sourceName.c_str(), std::ios::app);\n            std::ofstream header(headerName.c_str(), std::ios::app);\n\n            source << "#include \\"" << headerName << "\\"";\n\n            //Header is slightly more involved because we want all capital letters\n            for (std::string::size_type i=0; i<headerName.length(); ++i){\n\n                if(headerName[i] == \'.\'){\n\n                    headerName[i] = \'_\';\n                }\n                else{\n\n                    headerName[i] = toupper(headerName[i]);\n                }\n            }\n\n            header  << "#ifndef __" << headerName << "\\n"\n                    << "#define __" << headerName << "\\n"\n                    << "\\n"\n                    << "\\n"\n                    << "#endif";\n\n            source.close();\n            header.close();\n\n        }\n\n    }\n\n    return returnValue;\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Die*_*ühl 5

流无法知道您将要阅读什么。在尝试读取后,您始终需要检查读取尝试是否成功:

while(issClassNames >> sClassName) {
    // do whatever processing of sClassName you want to do
}
Run Code Online (Sandbox Code Playgroud)