Feelsbadman
Feelsbadman

Reputation: 1165

C++ <algorithm> replace all occurrences not working

I am trying to make a simple word-guessing game in C++(11) and since it requires a "hidden word" to be shown to the player I have made two strings; 1st one visible "test", and other one just filled with underscores "____". I have made a loop with string::iterator to compare each character in the string with given (char) input from user.

The problem here, is that it doesn't replace all occurrences in the string the very first time I give an input of 't' which should result in "t__t" instead of "t___"

for the process I am using the replace function from <algorithm> header which looks like this:

replace(str.begin(), str.end(), str.at(char_idx), newLetter);


and this function is inside a for loop which iterates through string; In another function called checkLetter()

void checkLetter(char &newLetter, string &randstr, int &strlen, string &hiddenWord){
    string::iterator it;
    char char_idx;
    for(auto it = randstr.begin(); it != randstr.end();++it){
        if(*it == newLetter){
            char_idx=randstr.find(*it);
            replace(hiddenWord.begin(), hiddenWord.end(), hiddenWord.at(char_idx), newLetter);
        }
    }
    cout << hiddenWord << endl;
}

now this is what the output looks like:

The Word is 4 letters long.

t
t___
e
te__
s
tes_
t
tes_

but when i ran a simpler version of that such as

string RandomWord = "test";
replace(RandomWord.begin(),RandomWord.end(),'t','S');
cout << RandomWord << endl;

which gives 'SesS'

All of the code:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

// Function Declaration
string getRandWord();
void checkLetter(char&, string&, int&, string&);

int main() {
//    string RandomWord = getRandWord();
    string RandomWord = "test";
    string hiddenWord = "";

    unsigned long int _length_ = RandomWord.length();
    int chances = int(_length_)+1;
    char newLetter;

    hiddenWord.append((_length_),'_');
    cout << "The Word is "<< _length_ <<" letters long." << endl;
    while(chances > 0){
        cin >> newLetter;
        checkLetter(newLetter, RandomWord, chances, hiddenWord);

        chances--;
    }

    return 0;
}


// Functions

void checkLetter(char &newLetter, string &randstr, int &strlen, string &hiddenWord){
    string::iterator it;
    char char_idx;
    for(auto it = randstr.begin(); it != randstr.end();++it){
        if(*it == newLetter){
            char_idx=randstr.find(*it);
            replace(hiddenWord.begin(), hiddenWord.end(), hiddenWord.at(char_idx), newLetter);
        }
    }
    cout << hiddenWord << endl;
}

string getRandWord(){
    string filePath = "/Users/nedimkanat/XCODE/testcpp/testcpp/";
    enum sizes {
        ARRAY_SIZE = 5
    };
    // set seed
    srand((unsigned)time(0));

    // get random int between 0 and 5
    int randint = rand() % ARRAY_SIZE;

    // str to store each line from file
    string str;

    // array to store 5 (random) words
    vector<string> arr;

    // initialize file object & open file
    ifstream file(filePath+"words.txt");
    int counter = 0;

    // loop trough file
    if (file.is_open()){
        while (getline(file,str) && counter < ARRAY_SIZE){
            arr.push_back(str);
            counter++;
        }
        file.close();
    } else {
        cout << "File is not open" << endl;
    }

    // send away random word
    if(arr.empty()){
        cout << "CANCER" << endl;
    }
    return arr.at(randint);
}

Upvotes: 0

Views: 342

Answers (2)

mattn
mattn

Reputation: 7723

If based on your code, you shouldn't use std::replace since the third argument (meaning replace-from letter) can be appear twice in the text. So you can just replace the letter like below.

void checkLetter(char &newLetter, string &randstr, int &strlen, string &hiddenWord){
    string::iterator it;
    for(auto it = randstr.begin(); it != randstr.end();++it){
        if(*it == newLetter){
            hiddenWord.at(it - randstr.begin()) = newLetter;
        }
    }
    cout << hiddenWord << endl;
}

Upvotes: 1

Some programmer dude
Some programmer dude

Reputation: 409364

If I understand you correctly you basically want to transform one string into another?

Using the two-input-iterator overload of std::transform should be the function you need. The first input container could be the string with underscores that you want to replace, while the second input container is the string containing the actual word. Then you provide a functor or lambda using the input letter as a state or capture, and have the function return the current letter from the first container (the masked word) unless the letter is matching the current letter in the second container (the word to be guessed). The output iterator is the masked word (same as the first input iterator).

Perhaps something like this example program shows:

#include <iostream>
#include <string>
#include <algorithm>

int main()
{
    std::string word = "foobar";  // The word to guess
    std::string masked = "______";  // The masked word, that will be unmasked letter by letter

    // "Guess" some letters...        
    for (auto const letter : { 'f', 'a', 'x', 'o', 'y', 'r', 'b' })
    {
        std::cout << "Before '" << letter << "': \"" << masked << "\"\n";

        // Convert the masked word into a less masked word
        // But only if the letter is found in the word to guess
        std::transform(std::begin(masked), std::end(masked), std::begin(word),
                      std::begin(masked),
                      [letter](char const& masked_letter, char const& word_letter)
                       {
                           if (letter == word_letter)
                               return word_letter;
                           else
                               return masked_letter;
                       });

        std::cout << "After  '" << letter << "': \"" << masked << "\"\n";
    }
}

Example of it running here.

Upvotes: 2

Related Questions