从文件中读取的结果并不返回正确数量的对象。

huangapple go评论58阅读模式
英文:

Reading from a file is not returning the correct number of objects

问题

以下是您提供的代码部分的中文翻译:

if (event.command.get_command_name() == "change")
{
    int count = 0;
    vector<string> entities;
    vector<int> health;
    string myText;
    string nameChoice = get<string>(event.get_parameter("user"));
    ifstream MyReadFile("entities.txt");
    while (getline(MyReadFile, myText, ':'))
    {
        if (count == 0)
        {
            entities.push_back(myText);
            count += 1;
        }
        else if (count == 1)
        {
            if (entities.back() == nameChoice)
            {
                int temp = stoi(myText);
                temp += get<int64_t>(event.get_parameter("numeral"));
                cout << "temp" << temp;
                health.push_back(temp);
                count = 0;
            }
            else
            {
                health.push_back(stoi(myText));
                count = 0;
            }
        }
    }
    MyReadFile.close();
    ofstream MyFile("entities.txt", ios_base::out);
    for (int i = 0; i < entities.size(); i++)
    {
        MyFile << entities[i] << ":";
        MyFile << health[i] << "\n";
    }

    MyFile.close();
}

请注意,这是代码的中文翻译部分。如果您需要进一步的帮助或有其他问题,请随时提出。

英文:

I am coding a Discord bot, using the D++ library, that tracks the amount of health an entity has, and allows users on the server to modify the health of said entities.

My problem is with my command that handles when a user wants to modify the health value. The contents of the .txt file are as follows:

Zombie:120
Skeleton:200
Vampire:500

If the user enters the command to modify the Vampire's health by -100, the file will instead look like this instead of lowering the vampire's health from 500 to 400:

Zombie:120
200
Vampire:500

Here is the relevant code:

if (event.command.get_command_name() == &quot;change&quot;)
{
    int count = 0;
    vector&lt;string&gt; entities;
    vector&lt;int&gt; health;
    string myText;
    string nameChoice = get&lt;string&gt;(event.get_parameter(&quot;user&quot;));
    ifstream MyReadFile(&quot;entities.txt&quot;);
    while(getline(MyReadFile, myText, &#39;:&#39;))
    {
        if (count == 0)
        {
            entities.push_back(myText);
            count += 1;
        }
        else if (count == 1)
        {
            if (entities.back() == nameChoice)
            {
                int temp = stoi(myText);
                temp += get&lt;int64_t&gt;(event.get_parameter(&quot;numeral&quot;));
                cout &lt;&lt; &quot;temp&quot; &lt;&lt; temp;
                health.push_back(temp);
                count = 0;
            }
            else
            {
                health.push_back(stoi(myText));
                count = 0;
            }
        }
    }
    MyReadFile.close();
    ofstream MyFile(&quot;entities.txt&quot;, ios_base::out);
    for (int i = 0; i &lt; entities.size(); i++)
    {
        MyFile &lt;&lt; entities[i] &lt;&lt; &quot;:&quot;;
        MyFile &lt;&lt; health[i] &lt;&lt; &quot;\n&quot;;
    }
    
    MyFile.close();
}

Through some debugging, I have found out that the while loop is only looping 4 times, when it seemingly should be doing so 6 times.

As I am still a beginner at C++, I have been struggling to understand why, so I'd appreciate any guidance you could offer.

答案1

得分: 4

Sure, here is the translated content:

while(getline(MyReadFile, myText, ':'))

What's happening here is a demonstration of the Golden Rule Of Computer Programming, which says:

"Your computer always does exactly what you tell it to do instead of what you want it to do."

Here, you told your computer: "read from myReadFile until the next : character". So this is what your computer does here.

If the contents of the file are:

zombie:100
skeleton:200
mummy:300

Then:

  • The first time in this while loop, your computer reads "zombie" and puts that into your myText (everything up until the first :).

  • The second time in this while loop, it reads "100\nskeleton" and puts that into your myText (everything up until the second :).

  • The third time in this while loop, it reads "200\nmummy" and puts that into your myText (everything up until the third :).

  • The fourth time in this while loop, it reads "300" and puts that into your myText (everything up until the end-of-file).

This is what you told your computer to do: read until the next : character, every time, so that's what your computer does.

It's pretty clear that this is not what you wanted your computer to do, but that's what you told it to do, so that's what it does.

I have been struggling to understand why

That's why, for the reasons explained above. std::getline() reads input until a specific character gets read. End of story. No exceptions. By default it's the \n character, but an optional third parameter specifies a different character. If it is specified it completely replaces the default \n character, it does not supplement it.

英文:

> c++
&gt; while(getline(MyReadFile, myText, &#39;:&#39;))
&gt;

What's happening here is a demonstration of the Golden Rule Of Computer Programming, which says:

"Your computer always does exactly what you tell it to do instead of what you want it to do."

Here, you told your computer: "read from myReadFile until the next : character". So this is what your computer does here.

If the contents of the file are:

zombie:100
skeleton:200
mummy:300

Then:

  • The first time in this while loop, your computer reads &quot;zombie&quot; and puts that into your myText (everything up until the first :).

  • The second time in this while loop, it reads &quot;100\nskeleton&quot; and puts that into your myText (everything up until the second :).

  • The third time in this while loop, it reads &quot;200\nmummy&quot; and puts that into your myText (everything up until the third :).

  • The fourth time in this while loop, it reads &quot;300&quot; and puts that into your myText (everything up until the end-of-file).

This is what you told your computer to do: read until the next : character, every time, so that's what your computer does.

It's pretty clear that this is not what you wanted your computer to do, but that's what you told it to do, so that's what it does.

> I have been struggling to understand why

That's why, for the reasons explained above. std::getline() reads input until a specific character gets read. End of story. No exceptions. By default it's the \n character, but an optional third parameter specifies a different character. If it is specified it completely replaces the default \n character, it does not supplement it.

答案2

得分: 2

以下是翻译好的代码部分:

如果你感到有必要使用并行数组来保存相关的值,你应该使用一个保存`struct`/`class`类型的单一数组。

尽管如此,你没有正确使用`std::getline()`。你告诉它只读取到下一个`:`字符,而没有考虑换行符。你期望它在遇到*要么*`':'` *要么* `'\n'`时停止读取,但实际上它并不是这样工作的。

在处理*以行为基础的数据*时,你应该首先读取整行,然后根据需要解析该行。

可以尝试像这样改进代码:

if (event.command.get_command_name() == "change")
{
    struct entity
    {
        string name;
        int health;
    };

    vector<entity> entities;
    string line, nameChoice = get<string>(event.get_parameter("user"));

    ifstream MyReadFile("entities.txt");
    while (getline(MyReadFile, line))
    {
        istringstream iss(line);

        entity e;
        getline(iss, e.name, ':');
        iss >> e.health;

        if (e.name == nameChoice)
        {
            e.health += get<int64_t>(event.get_parameter("numeral"));
            cout << "temp" << e.health;
        }

        entities.push_back(e);
    }
    MyReadFile.close();

    ofstream MyFile("entities.txt");
    for (size_t i = 0; i < entities.size(); ++i)
    {
        MyFile << entities[i].name << ':' << entities[i].health << '\n';
    }
    
    MyFile.close();
}

或者,如果你在读取每个健康值后直接使用std::istringstream可能会更简单,例如:

if (event.command.get_command_name() == "change")
{
    struct entity
    {
        string name;
        int health;
    };

    vector<entity> entities;
    string nameChoice = get<string>(event.get_parameter("user"));
    entity e;

    ifstream MyReadFile("entities.txt");
    while (getline(MyReadFile, e.name, ':'))
    {
        MyReadFile >> e.health;
        MyReadFile.ignore(numeric_limits<streamsize>::max(), '\n');

        if (e.name == nameChoice)
        {
            e.health += get<int64_t>(event.get_parameter("numeral"));
            cout << "temp" << e.health;
        }

        entities.push_back(e);
    }
    MyReadFile.close();

    ofstream MyFile("entities.txt");
    for (size_t i = 0; i < entities.size(); ++i)
    {
        MyFile << entities[i].name << ':' << entities[i].health << '\n';
    }
    
    MyFile.close();
}

或者,如果你在读取原始文件的同时写入一个单独的文件,可以完全摒弃std::vector,例如:

if (event.command.get_command_name() == "change")
{
    string nameChoice = get<string>(event.get_parameter("user"));
    string entity;
    int health;

    ifstream MyReadFile("entities.txt");
    ofstream MyFile("new_entities.txt");

    while (getline(MyReadFile, entity, ':'))
    {
        MyReadFile >> health;
        MyReadFile.ignore(numeric_limits<streamsize>::max(), '\n');

        if (entity == nameChoice)
        {
            health += get<int64_t>(event.get_parameter("numeral"));
            cout << "temp" << health;
        }

        MyFile << entity << ':' << health << '\n';
    }

    MyReadFile.close();
    MyFile.close();

    remove("entities.txt");
    rename("new_entities.txt", "entities.txt");
}
英文:

Any time you feel the need to use parallel arrays to hold related values, you should instead use a single array that holds a struct/class type.

That said, you are not using std::getline() correctly. You are telling it to read only until the next : character, without any regards to line breaks. You are expecting it to stop reading when it encounters either &#39;:&#39; or &#39;\n&#39;, but that is simply not how it works.

When dealing with line-oriented data, you should read a whole line first, and then parse the line as needed.

Try something more like this:

if (event.command.get_command_name() == &quot;change&quot;)
{
    struct entity
    {
        string name;
        int health;
    };

    vector&lt;entity&gt; entities;
    string line, nameChoice = get&lt;string&gt;(event.get_parameter(&quot;user&quot;));

    ifstream MyReadFile(&quot;entities.txt&quot;);
    while (getline(MyReadFile, line))
    {
        istringstream iss(line);

        entity e;
        getline(iss, e.name, &#39;:&#39;);
        iss &gt;&gt; e.health;

        if (e.name == nameChoice)
        {
            e.health += get&lt;int64_t&gt;(event.get_parameter(&quot;numeral&quot;));
            cout &lt;&lt; &quot;temp&quot; &lt;&lt; e.health;
        }

        entities.push_back(e);
    }
    MyReadFile.close();

    ofstream MyFile(&quot;entities.txt&quot;);
    for (size_t i = 0; i &lt; entities.size(); ++i)
    {
        MyFile &lt;&lt; entities[i].name &lt;&lt; &#39;:&#39; &lt;&lt; entities[i].health &lt;&lt; &#39;\n&#39;;
    }
    
    MyFile.close();
}

Alternatively, you can omit the std::istringstream if you instead simply ignore() the next line break after reading each health value, eg:

if (event.command.get_command_name() == &quot;change&quot;)
{
    struct entity
    {
        string name;
        int health;
    };

    vector&lt;entity&gt; entities;
    string nameChoice = get&lt;string&gt;(event.get_parameter(&quot;user&quot;));
    entity e;

    ifstream MyReadFile(&quot;entities.txt&quot;);
    while (getline(MyReadFile, e.name, &#39;:&#39;))
    {
        MyReadFile &gt;&gt; e.health;
        MyReadFile.ignore(numeric_limits&lt;streamsize&gt;::max(), &#39;\n&#39;);

        if (e.name == nameChoice)
        {
            e.health += get&lt;int64_t&gt;(event.get_parameter(&quot;numeral&quot;));
            cout &lt;&lt; &quot;temp&quot; &lt;&lt; e.health;
        }

        entities.push_back(e);
    }
    MyReadFile.close();

    ofstream MyFile(&quot;entities.txt&quot;);
    for (size_t i = 0; i &lt; entities.size(); ++i)
    {
        MyFile &lt;&lt; entities[i].name &lt;&lt; &#39;:&#39; &lt;&lt; entities[i].health &lt;&lt; &#39;\n&#39;;
    }
    
    MyFile.close();
}

Alternatively, you can get rid of the std::vector altogether if you write to a separate file while reading the original file, eg:

if (event.command.get_command_name() == &quot;change&quot;)
{
    string nameChoice = get&lt;string&gt;(event.get_parameter(&quot;user&quot;));
    string entity;
    int health;

    ifstream MyReadFile(&quot;entities.txt&quot;);
    ofstream MyFile(&quot;new_entities.txt&quot;);

    while (getline(MyReadFile, entity, &#39;:&#39;))
    {
        MyReadFile &gt;&gt; health;
        MyReadFile.ignore(numeric_limits&lt;streamsize&gt;::max(), &#39;\n&#39;);

        if (entity == nameChoice)
        {
            health += get&lt;int64_t&gt;(event.get_parameter(&quot;numeral&quot;));
            cout &lt;&lt; &quot;temp&quot; &lt;&lt; health;
        }

        MyFile &lt;&lt; entity &lt;&lt; &#39;:&#39; &lt;&lt; health &lt;&lt; &#39;\n&#39;;
    }

    MyReadFile.close();    
    MyFile.close();

    remove(&quot;entities.txt&quot;);
    rename(&quot;new_entities.txt&quot;, &quot;entities.txt&quot;);	
}

huangapple
  • 本文由 发表于 2023年4月4日 06:50:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/75924281.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定