简体   繁体   中英

string iterator not decrementable

I have an issue with string::iterator . VS says string iterator not decrementable . My first project works fine with the same function 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);
}

But if I load some words from a .txt and apply Is_Palindrome function it crashes, but if I change string::iterator to a simple loop with a [ ] access it works correct. Here the problem code.

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

The problem in your code is

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

First, if you want to check a character you should use ' not " . So your statement should be

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

Secondly, you are using erase() . When you call erase it invalidates all references and iterators to the current element to the end. Since you are using the same iterator that you used to delete the element this is undefined behavior . Just because it works in the first example doesn't mean it will work in another piece of code. Luckily erase() returns an iterator to the element after the erased element. We can capture that iterator and use that for the next iteration. In order to do this, you need to change your for loop into a while loop like:

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM