【问题标题】:Problems with longer getline cin inputs较长的 getline cin 输入的问题
【发布时间】:2013-09-23 12:45:46
【问题描述】:

我正在编写一个 C++ 程序,它要求用户输入一个单词或句子,遍历单词/句子,用 'aoa' 或 'AoA' 替换所有 'a' 或 'A' 实例,然后输出结果。但是,如果我尝试输入更长的句子,我会遇到问题。例如,如果我输入“程序为什么不运行”,程序会输出奇怪的字母而不是预期的结果。 这是我的代码:

#include <iostream>
#include <string>

using namespace std;

int main(int argc, const char * argv[])
{
string mening, temp; //The mening string is the word/sentence the user will input.
int play = 1, add;

while (play == 1) {
cout<<"Type in the sentence: ";

getline(cin, mening); //The input is saved in the string variable mening.

unsigned long y = mening.size(); //Grabs the amount of characters in input; this number is saved in the unsigned long variable y.
add = 0; //Makes sure the int variable add is reset to 0 if the loop restarts.

for (int k = 0, n = 1;n<=y;k++, n++) {
    if (mening[k] == 'a' || mening[k] == 'A') {
        k++;


        for (int i = k, m = 1;m<=y - n;i++, m++) {
            temp[i] = mening[i];
        } //The characters after the one that has been checked are stored in temp array indexes, if the character that has been checked is an a or A.

        for (int i = k, m = 1, j = k + 2;m<=y - n;i++, m++, j++) {
            mening[j] = temp[i];
        } //The characters after the one that has been checked move two steps to the right, to allow the two extra letters.

        mening[k] = 'o';
        mening[k + 1] = mening[k - 1];

        k++;

        add = add + 2; //The int variable add is increased by 2 during each aoa/AoA to avoid strange characters being outputted at the very end.

    }
    else { }

}

for (int k = 0;k<=y + add - 1;k++) {
    cout<<mening[k];
}

cout<<endl<<"Do you want to do it again? (yes/no): ";

getline(cin, mening);

    cin.clear();
    cout << flush;
    cout.flush();
    cout.clear();

if (mening == "Yes" || mening == "yes" || mening == "YES") {

}
else {
    play = 2;
}
}


cout<<endl<<"The program will now close.";

return 0;
}

什么可能导致问题?

【问题讨论】:

  • 我会坐下来重新考虑你的算法。这个想法很复杂。您只需要迭代一次字母并构建一个新字符串。
  • 我敢打赌你正在写掉字符串的结尾。使用内置的字符串成员函数来操作其内容。 (如插入或擦除)。
  • 附言。代码格式化很重要。请使其易于阅读。
  • Loki:关于代码格式,我想知道您具体指的是什么。顺便说一句,这是我在 Stack Overflow 上的第一篇文章。

标签: c++ input getline cin


【解决方案1】:

一个直接的问题是您从[1,n] 索引, 而 C++(std::stringstd::vector,还有 C 风格 数组)使用[0,n)。这意味着您将访问 字符串的结尾。你将字符存储到temp 使用temp[x],尽管temp 的大小始终为0。两者 其中一些是未定义的行为,可能会产生任何影响 (包括使程序崩溃)。

你应该使用标准库的调试模式 开发代码。在 Visual Studios 中,我认为这是 默认;使用 g++,您需要将 -D_GLIBCXX_CONCEPT_CHECKS -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC 添加到您的命令中 线。

处理这个最简单的方法是复制到一个新的字符串中, 随时进行更改:

std::string results;
for ( auto current = mening.cbegin(); current != mening.cend(); ++ current ) {
    switch ( *current ) {
    case 'a':
        results += "aoa";
        break;

    case 'A':
        results += "AoA";
        break;

    default:
        results += *current;
        break;
    }
}

如果您确实想就地更换,那就很棘手了。 当您插入比开始时更多的文本时,迭代器是 无效。所以你需要这样的东西:

static std::string const Ao( "Ao" );
static std::string const ao( "ao" );
for ( auto current = mening.begin(); current != mening.end(); ++ current ) {
    switch ( *current ) {
    case 'a':
        current = mening.insert( current, ao.begin(), ao.end() ) + 2;
        break;

    case 'A':
        current = mening.insert( current, Ao.begin(), Ao.end() ) + 2;
        break;
    }
}

就个人而言,我更喜欢复制到一个新的字符串中。

【讨论】:

  • @LokiAstari 我建议你看一下代码。我在insert 之后使用insert 返回的迭代器。 (另一方面,我没有得到正确的返回值。我会纠正它。)
  • James,你的 cmets 很棒。我不知道你必须有一个字符串的大小才能为它分配数组值,也不知道你必须从 0 开始分配,因为你不必在使用字符串时声明数组。如果这是一个非常愚蠢的问题,我很抱歉。
  • @MånsNilsson 这一点都不傻。似乎很多人在开始时都会犯同样的错误。但这就是我们使用+=(或向量上的push_back)之类的原因。这些函数会自动增长字符串。
  • 詹姆斯,我已经接受了你的第一个建议。当您不妨将每个字母复制到一个新字符串中时,为什么还要经历重新排列数组数字等的所有痛苦?我不知道您可以添加到字符串中。现在我只有 60 行代码,而且效果很好!好吧,很明显我仍然是编程的初学者。但希望我会在下一个项目中考虑到这些事情。 :)
  • @MånsNilsson 我自己通常在第一个解决方案中使用变体。当我尝试第二个时,我对它减少了代码行感到有些惊讶,但我仍然发现第一个更清晰,更易于理解和修改。 (第二个困惑的是 Loki Astari,他是这里的常客,并且非常了解 C++。将迭代器放入正在变异的容器中的规则有些复杂。)
【解决方案2】:

我建议您应该使用标准函数std::string::insert 插入字符'oA''oa'。它将使您的代码更易于处理和调试。

或者你可以简单地这样做:

#include <iostream>
#include <string>

using namespace std;

int main(int argc, const char * argv[])
{
string mening, temp; //The mening string is the word/sentence the user will input.
int play = 1, add;

while (play == 1) {
cout<<"Type in the sentence: ";

getline(cin, mening); //The input is saved in the string variable mening.

unsigned long y = mening.size(); //Grabs the amount of characters in input; this number is saved in the unsigned long variable y.
add = 0; //Makes sure the int variable add is reset to 0 if the loop restarts.

for (int n = 0; n<y; n++ ) {
    cout << mening[n];
    if (mening[n] == 'a' || mening[n] == 'A') 
        cout << "o" << mening[n];
}

cout<<endl<<"Do you want to do it again? (yes/no): ";

getline(cin, mening);

    cin.clear();
    cout << flush;
    cout.flush();
    cout.clear();

if (mening == "Yes" || mening == "yes" || mening == "YES") {

}
else {
    play = 2;
}
}


cout<<endl<<"The program will now close.";

return 0;
}

【讨论】:

  • 是的,我认为没有必要存储新句子。处理时输出即可。
【解决方案3】:
#include <iostream>
#include <string>


std::string ReplaceA(std::string s) {
    std::string temp = "";
    for (unsigned int k = 0; k < s.size(); k++) {
        if (s[k] == 'a' || s[k] == 'A') {
            temp = temp + s[k];
            temp = temp + "o";
            temp = temp + s[k];
            }
            else { 
                temp = temp + s[k];
            }

        }
    return temp;
}

int main(int argc, const char * argv[])
{
    std::string mening; //The mening string is the word/sentence the user will input.
    int play = 1;

    while (play == 1) {
        std::cout<<"Type in the sentence: ";

        getline(std::cin, mening); //The input is saved in the string variable mening.

        std::cout << ReplaceA(mening) << std::endl;

        std::cout << "Do you want to do it again? (yes/no): ";

        std::cin >> mening;
        if( std::cin.fail() || ( mening != "yes" && mening != "no" ) ) {
            std::cout << "Bad Input\nDo you want to do it again? (yes/no): ";
            std::cin.clear();
            std::cin.ignore('256','\n');
            std::cin >> mening;
        }else{
            if(mening == "no") break;
        }
        std::cin.clear();
        std::cin.ignore('256','\n');

    }


    std::cout << "The program will now close.";

    return 0;
}

更有条理,带有处理返回字符串的转换的函数。

你也可以试试这个功能,两者都可以,一个看起来更漂亮一点。

std::string ReplaceA(std::string s) {
    std::string temp = "";
    for(std::string::iterator k = s.begin(); k != s.end(); k++) {
        switch(*k) {
        case 'a': temp += "aoa"; break;
        case 'A': temp += "AoA"; break;
        default: temp += *k; break;
        }
    }
    return temp;
}

【讨论】:

  • 将转换分解成一个单独的函数是个好主意。但只是一个建议:在返回值上使用+=,而不是更复杂的temp = temp + ...
  • 是的,正在追踪一个错误。 temp += s[k] + "o" + s[k]; 给了我意想不到的输出,所以我把它写了很长时间来修复它。可能s[k]+"o"+s[k] 是问题
  • 说实话,我只是用一个开关,把'a''A'分开处理,所以我可以写temp += "aoa";temp += "AoA";。就个人而言,这似乎有点简单。反正对我来说。 (一旦您停止尝试就地进行更改,并将它们分解到一个单独的函数中,就像您所做的那样,细节就真的不那么重要了。)
【解决方案4】:

实际上最后一个cin还剩下一个尾随字符'\n'>>,所以getline取这个'\n'字符并被终止;在getline之前忽略buffer的内容...

cin.ignore();
getling(cin, string_name)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-14
    • 2016-02-05
    • 1970-01-01
    • 2021-08-26
    • 2014-06-22
    相关资源
    最近更新 更多