Std::istringstream.good()比预期多返回一次true

std::istringstream.good() returns true once more than expected

本文关键字:返回 true 一次 istringstream good Std      更新时间:2023-10-16

我正在制作一个玩具程序,从字符串创建类(例如,我输入它"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. 在以ofstream模式打开文件之前,测试以ifstream模式打开文件是否已经存在。(问题:没有解决运行while一次模式的问题,而不是必要的)
  2. 删除追加模式。(问题:有可能覆盖已经存在的类,仍然不能解决while-loop问题)
  3. 在while-loop中使用条件测试,不要在上次运行时打开文件,例如,比较当前类名和上次类名并终止如果相等。问题:不确定如何检查这是否是最后一次运行,除了模糊的"这是上一次相同的类名吗?"测试,如果输入字符串是"test1 test1 test2",也可能有过早中止的风险

编辑

我已经意识到:并不是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.

EDIT 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;
}

流不知道你要读什么。如果读取尝试成功,您总是需要在之后检查:

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

尝试更改:

while(issClassNames.good()){

:

while(issClassNames >> sClassName){

并将这一行从循环的顶部取出

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

你应该替换

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

while(issClassNames >> sClassName) {

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

operator >>返回流本身的句柄(可转换为bool),可以在condition中使用。因此,只有当读取成功时才会进入循环。