std :: istringstream.good()返回true一次超过预期

时间:2014-05-26 07:33:38

标签: c++ string while-loop

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

此功能发生在此功能中:

bool makeClassesFromString(std::string classNames){

    bool returnValue = true;

    if (classNames == ""){

        returnValue = false;

    }
    else{

        std::istringstream issClassNames (classNames);
        std::string sClassName;

        while(issClassNames.good()){

            issClassNames >> sClassName;
            std::string sourceName = sClassName;
            sourceName += ".cpp";

            std::string headerName = sClassName;
            headerName += ".h";

            std::ofstream source(sourceName.c_str()), std::ios::app);
            std::ofstream header(headerName.c_str()), std::ios::app);

            //source << "#include \"" << headerName << "\"";


            source.close();
            header.close();

        }

    }

    return returnValue;

}

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

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

在上面的代码示例中,我已经注释掉了我开始处理这个问题的行,但是当我不对它进行评论时,我会得到这样的行为:

input:
classmaker.exe test1 test2

output:
test1.h
test2.h
test1.cpp //(Which contains: #include "test1.h")
test2.cpp //(Which contains: #include "test2.h"#include "test2.h"

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

到目前为止提出的解决方案:

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

    修改

    我已经意识到:istringstream.good()并不是真的再次返回true,而是它的评估结果如下:

    input: "test1 test2"
    
    evaluation:
    good = true; extract next to string //(succeeds, overwrites empty string with "test1")
    good = true; extract next to string //(succeeds, overwrites "test1" with "test2")
    good = true; extract next to string //(fails, Doesn't overwrite "test2")
    good = false; break loop.
    

    编辑2 + 3

    那很容易解决。谢谢你们所有人。不幸的是,我只能将一个答案标记为已接受,所以我选择了第一个发布的答案,但我感谢您的帮助。

    最终的整个计划:

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

3 个答案:

答案 0 :(得分:4)

流无法知道您要阅读的内容。您总是需要在之后检查,如果您的读取尝试成功,则尝试阅读

while(issClassNames >> sClassName) {
    // do whatever processing of sClassName you want to do
}

答案 1 :(得分:2)

你应该替换

while(issClassNames.good()){
  issClassNames >> sClassName;

while(issClassNames >> sClassName) {

为什么
假设您的输入为test1 test2。你先检查流是好的。由于没有错误,因此您可以阅读test1中的sClassName。现在流仍然很好,所以你继续阅读test2中的sClassName(此步骤中没有错误)。流仍然很好所以你继续尝试再次阅读,但operator >>失败,sClassName保持不变,因此你看到旧的值test2

operator >>返回流本身的句柄(可转换为bool),我们可以在条件中使用。所以只有在读取成功的情况下才会进入循环。

答案 2 :(得分:2)

尝试更改:

while(issClassNames.good()){

为:

while(issClassNames >> sClassName){

将该行从循环顶部取出。

流运算符>>返回一个流,可以将其强制转换为bool,结果为good()的结果。由于返回的流在调用>>之后处于状态,如果操作设置了eof位,它将返回false。