Going from one instance of string.find to the next

Viewed 82

Well, this is another classic puzzle... My goal is to take the first letter of each word in a string and put it at the end of that word adding -ay (Pig Latin). I managed to transform the first word. But how do I move on to the next? I consulted different sources, forums but I am still stuck... Any tips? :) Maybe something like: pos = a.find(" ", pos +1)? See code below:

#include <iostream>
#include <string>


int main()

{
    std::string a = "hello what is going on";
    std::string b = "ay ";
    std::size_t pos = a.find(" ");
    int length = a.length();
    std::string first = a.substr(0,1);

        for (int i = 0; i <= length; i++)
        {   
            if (pos != std::string::npos)
                {
                a.replace(pos, a.length(), first + b);   //I guess I have to change sth. here.
                }                                        //Maybe a while-loop?
        }

    a.replace(0, 1, "");
    std::cout << a;            //Output: "ellohay"; **goal**: "ellohay hatway siay oingay noay"
    
}

4 Answers

As so often with string-manipulation tasks, it is so much easier to keep separate "source" and "destination" strings, rather than trying to modify a single string "on the fly".

For your case, the following PigLatin function does what you ask (though I'm not here attempting to validate your definition of Pig Latin). It also handles multiple spaces between words and has a code block to handle characters that aren't letters or spaces, which you can modify as you see fit.

#include <iostream>
#include <string>

std::string PigLatin(std::string & input)
{
//  if (input.empty()) return ""; // Not sure what you want to do with empty strings?
    std::string answer = "";
    bool inword = false;
    char firstc = ' '; // Never used unitialized, but give it a value to silence the warning
    for (auto testc : input) {
        if (std::isspace(testc)) { // Space: check if we have a current word...
            if (inword) {          // ... and add last char + "ay" if we do
                answer += firstc;
                answer += "ay";
            }
            inword = false;        // Flag that we are now outside a word
            answer += testc;       // And add this space to the answer.
        }
        else if (std::isalpha(testc)) { // Letter: Check if it's the first in a word...
            if (!inword) firstc = testc; // If so, store it for future use
            else answer += testc;        // Otherwise append to the answer
            inword = true;
        }
        else { // Non-alpha, non-space ...
            answer += testc;
        }
    }
    // Handle terminal case, where we have a word without a space after it:
    if (inword) {
        answer += firstc;
        answer += "ay";
    }
    return answer;
}

int main()
{
    std::string a = "hello ... what is   going on";
    std::string b = PigLatin(a);
    std::cout << b << std::endl;
    return 0;
}

Feel free to ask for further clarification and/or explanation.

It's pretty simple:

Just tokenize the string, take each token, save the first char of token in some var, remove the first char and build what you need.

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main() {
  // build istringstream object from provided text
  auto input = istringstream{"hello what is going on"};

  string token = "";
  string output = "";

  // tokenize provided text input
  while(input >> token) {
    char first = token[0]; // save the first char of token
    token.erase(0, 1); // remove the first char from token
    output = output + token + first + "ay" + " "; // build what you need
  }

  // okay, print output to check if everything is okay
  cout << output << endl;
  
  return 0;
}

And the output is:

ellohay hatway siay oinggay noay

[P.S.]: feel free to ask, if you have any question regarding this code/solution method.

Here's a simple way to do what you want using standard algorithms:

std::ostringstream result;  // to store the result

std::istringstream iss{a};  // convert string to stream

std::transform(std::istream_iterator<std::string>{iss}, 
               std::istream_iterator<std::string>{},    
               std::ostream_iterator<std::string>{result},
               [&b](auto const &word) {
                   return word.substr(1) + word[0] + b;   // pig latin
               });

std::cout << result.str();  // convert output stream to string             

Here's a demo.

Ok, so thanks to all of you I came up with some sort of "mixed" implementation of your ideas, like so:

#include <iostream>
#include <string>


int main()

{
    std::string a = "hello what is going on";
    std::string b = "ay ";
    std::string c = "ay";
    std::string delimiter = " ";
    std::size_t pos = 0;
    std::string token;
    std::string output = "";
    

    while ((pos = a.find(delimiter)) != std::string::npos)
    {
        token = a.substr(0, pos);
        char first = token[0];
        token.replace(pos, token.length(), first + b);
        token.erase(0, 1);
        std::cout << token << std::endl;
        a.erase(0, pos + delimiter.length());
    }
    char firstA = a[0];
    a.erase(0, 1);
    
    std::cout << a + firstA + c  << std::endl;
}

Not sure if advanced programmers would disapprove of that solution... Again: thank you very much!!

Related