字符串迭代器不可递减

时间:2015-08-13 11:24:43

标签: c++ c++11 iterator

我遇到了 string :: iterator 的问题。 VS说字符串迭代器不可递减。我的第一个项目使用相同的功能 Is_Palindrom

工作正常
 #include <iostream> 
#include <string> 
#include <vector>
#include <array>
#include <valarray>
#include <functional>
#include <iterator> 
#include <algorithm>
#include <iterator>
#include <cctype>

using namespace std;

string Is_Palindrom(string str)
{
    string::iterator iter = str.begin();

    transform(str.begin(), str.end(), str.begin(), tolower);

    for (iter; iter != str.end(); iter++)
    {
        if (ispunct(*iter) || *iter == *" ")
        {
            str.erase(iter);
            iter--;
        }
    }

    return str;
}

void main()
{
    ostream_iterator<string, char>out(cout, "\n");
    string tmp;
    vector<string>str;

    while (getline(cin, tmp) && tmp != "quit")
        str.push_back(tmp);

    transform(str.begin(), str.end(), out, Is_Palindrom);
}

但是,如果我从 .txt 加载一些单词并应用Is_Palindrome函数它会崩溃,但是如果我将 string :: iterator 更改为一个带有 [] 访问它的工作正确。 这是问题代码。

#include <cstdlib> 
#include <ctime> 
#include <vector>
#include <fstream>
#include <iostream> 
#include <string> 
#include <vector>
#include <array>
#include <valarray>
#include <functional>
#include <iterator> 
#include <algorithm>
#include <iterator>
#include <cctype>

using std::string;
using std::vector;
using std::cout;
using std::cin;
using std::tolower;
using std::endl;
using std::ifstream;

string Is_Palindrom(string str);

int main()
{
    vector <string> wordlist;

    std::srand(std::time(0));

    ifstream fin;
    fin.open("text.txt");
    if (fin.is_open() == false)
    {
        std::cerr << "Can't open file. Bye.\n"; // не удается открыть файл 
        exit(EXIT_FAILURE);
    }

    string item;
    int count = 0;
    getline(fin, item, ' ');
    wordlist.push_back(item);
    transform(wordlist.begin(), wordlist.end(), wordlist.begin(), Is_Palindrom);

    while (fin) // до тех пор, пока нет ошибок ввода 
    {
        cout << count << " : " << wordlist[count] << endl;
        ++count;
        getline(fin, item, ' ');
        wordlist.push_back(item);
        transform(wordlist.begin(), wordlist.end(), wordlist.begin(), Is_Palindrom);
    }
    cout << "Done\n";
    fin.close();

    char play;
    cout << "Will you play a word game? <y/n> "; // запуск игры в слова 
    cin >> play;
    play = tolower(play);
    while (play == 'y')
    {
        string target = wordlist[std::rand() % wordlist.size()];
        int length = target.length();
        string attempt(length, '-');
        string badchars;
        int guesses = 6;
        cout << "Guess my secret word. It has " << length
            << " letters, and you guess\n"
            << "one letter at a time. You get " << guesses
            << " wrong guesses.\n";
        cout << "Your word: " << "attempt" << endl; // вывод слова
        while (guesses > 0 && attempt != target)
        {
            char letter;
            cout << "Guess a letter: ";
            cin >> letter;
            if (badchars.find(letter) != string::npos || attempt.find(letter) != string::npos)
            {
                cout << "You already guessed that. Try again.\n";
                continue;
            }
            int loc = target.find(letter);
            if (loc == string::npos)
            {
                cout << "Oh, bad guess !\n";
                --guesses;
                badchars += letter; // добавить к строке 
            }
            else
            {
                cout << "Good guess!\n";
                attempt[loc] = letter;
                // Проверить, не появляется ли буква еще раз 
                loc = target.find(letter, loc + 1);
                while (loc != string::npos)
                {
                    attempt[loc] = letter;
                    loc = target.find(letter, loc + 1);
                }
            }
            cout << "Your word: " << attempt << endl;
            if (attempt != target)
            {
                if (badchars.length() > 0)
                    cout << "Bad choices: " << badchars << endl;
                cout << guesses << " bad guesses left\n";
            }
        }
        if (guesses > 0)
            cout << "That's right!\n";
        else
            cout << "Sorry, the word is " << target << " . \n";
        cout << "Will you play another? <y/n> ";
        cin >> play;
        play = tolower(play);
    }
    cout << "Bye\n";
    return 0;
}

string Is_Palindrom(string str)
{
    string::iterator iter = str.begin();

    //for (int i = 0; i < str.length(); i++)
    for (iter; iter != str.end(); iter++)
    {
        //if (ispunct(str[i]) || str[i] == *" ")
        if (ispunct(*iter) || *iter == *" ")
        {
            //str.erase(i, 1);
            //i--;
            str.erase(iter, iter+1);
            if (iter == str.end())
                break;
            iter--;
        }
    }

    return str;
}

1 个答案:

答案 0 :(得分:4)

代码中的问题是

if (ispunct(*iter) || *iter == *" ")
{
    str.erase(iter);
    iter--;
}

首先,如果你想检查一个角色,你应该使用'而不是"。所以你的 声明应该是

if (ispunct(*iter) || *iter == ' ')
//or even better
if (ispunct(*iter) || isspace(*iter))

其次,您正在使用erase()。当您调用erase时,它会使当前元素的所有引用和迭代器无效。由于您使用的是与用于删除元素相同的迭代器,因此 undefined behavior 。仅仅因为它在第一个例子中起作用并不意味着它将在另一段代码中起作用。幸运的是,erase()在擦除元素之后返回元素的迭代器。我们可以捕获该迭代器并将其用于下一次迭代。为此,您需要将for循环更改为while循环,如:

while(iter != str.end())
{
    if (ispunct(*iter) || isspace(*iter))
        iter = str.erase(iter);  // erase and don't increment as we are already on the next character
    else
        iter++; // increment since it was a valid character
}