Shea Hunter Belsky
Shea Hunter Belsky

Reputation: 3218

C++: Appending to a vector string

I'm writing a "pig latin" program; read input from the users (first name and last name,) make the input lowercase and change the name depending upon what was in the name. If the first letter (of both the first and last name) was a vowel, we're supposed to add "way" to the end of it.

If the first letter was a consonant, we were to take the first letter, move it to the end of the string and add "ay" to the end of it.

My code has been giving me errors when trying to add text to the end of the string. It says it can't convert the string to a character, and I'm not exactly sure what that means. It also says I can't use the output operand << for strings, even though I've used it before.

The errors occur with "strcpy" and the final code where I output the names.

37: error: cannot convert 'std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >' to 'char*' for argument '1' to 'char* strcpy(char*, const char*)'

47: error: cannot convert 'std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >' to 'char*' for argument '1' to 'char* strcpy(char*, const char*)'

54: error: no match for 'operator<<' in 'std::cout << first'

I just need some help fixing the errors and seeing where I went wrong. The full code is included.

#include <iostream>
#include <vector>
#include <cstring>
#include <string>
using namespace std;
int main()
{
    int q, s;
    char shea[] = "way";
    char gavin_stop_looking_at_ponies[] = "ay";
    vector <string> first;
    vector <string> last;
    cout << "Please enter your first name." << endl;
    for (int i = 0; i < first.size(); i++)
    {
        getline (cin, first[i]);
        string nfirst = first[i];
        while (nfirst[q])
        {
            nfirst[q] = tolower(nfirst[q]);
        }
        first[i] = nfirst;
    }
    cout << "Please enter your last name." << endl;
    for (int j = 0; j < last.size(); j++)
    {
        getline (cin, last[j]);
        string nlast = last[j];
        while (nlast[s])
        {
            nlast[s] = tolower(nlast[s]);
        }
        last[j] = nlast;
        }
    if ( (first[0] == "a") ||( first [0] == "e") || (first [0] == "i") || (first [0] == "o")     || (first [0] == "u"))
    {
        strcpy (first, "way");
    }
    else
    {
        first[first.size()] = first[0] + "ay";
    }
    
    if ( (last[0] == "a") ||( last [0] == "e") || (last [0] == "i") || (last [0] == "o") || (last [0] == "u"))
    {
        strcpy (last, "way");
    }
    else
    {
        last[last.size()] = last[0] + "ay";
    }
    cout << first << last << endl;
    return 0;
}

Upvotes: 0

Views: 4835

Answers (4)

alpartis
alpartis

Reputation: 1122

I took a quick stab at writing the code for this just to play with it a bit. Here's what I came up with -- I think it's pretty well simplified, clean, and easy to understand:

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

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* simple pig-latin program ... converts a given "first name" and "last name"*/
/* from the user to pig latin translations.  Obviously, the so-called names  */
/* are really just any 2 random words, but that's the theme anyway.          */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


string xlat_to_piglatin(const string source)
{
    string result;

    //
    // translation: if word begins with a vowel, simply append "way" to
    //  it.  Otherwise, move the initial consonant from the beginning to
    //  the end, and append "ay" to it.
    //
    if (source.find_first_of("aeiou") == 0) {
        result = source;
        result.append("way");
    } else {
        result = source.substr(1, source.size());
        result.append(source.substr(0, 1));
        result.append("ay");
    }
    return result;
}

int main()
{
    string  first;
    string  last;

    //
    // get the first and last names from the user on standard in ...
    //
    cout << "Please enter your first name:" << endl;
    cin >> first;
    cout << "Please enter your last name:" << endl;
    cin >> last;

    //
    // convert strings to lower case ...
    //
    transform(first.begin(), first.end(), first.begin(), ptr_fun<int, int>(tolower));
    transform(last.begin(), last.end(), last.begin(), ptr_fun<int, int>(tolower));

    cout << xlat_to_piglatin(first) << " " << xlat_to_piglatin(last) << endl;
    return 0;
}

Upvotes: 0

alpartis
alpartis

Reputation: 1122

I agree with some of the previous responders, but I don't want to get into a detailed code review because it would be large and might miss the point. I think the problem you're having is a symptom of a underlying fundamental misunderstanding of the correct use of the C++ standard library. Mostly, you're mixing standard C library string implementations with the C++ "string" object. In C, there is a collection of functions that take character pointers and operate on them as strings while in C++ there is a string object that encapsulates a string of characters and provides a collection of operations on it. In some cases, the C++ string can be used where a C char * would normally exist, but in most cases it cannot.

Even in the C++ STL (standard template library) you're getting things mixed up a bit. Based on your code, it looks like you want to retreive the user's first name and last name i.e. 2 separate strings. However, you are declaring 2 collections (vectors in this case) of strings rather than just two individual strings themselves. Many other problems in your program follow from that.

I think you might like the information found here: http://en.cppreference.com/w/

Upvotes: 0

Jon Purdy
Jon Purdy

Reputation: 54971

I have annotated your code with explanations of some problems and suggestions for solutions. If there’s anything you don’t understand, comment and I’ll try to clarify.

#include <iostream>

// You don't need 'vector' for this.
#include <vector>

// You won’t often need the C string header in C++.
#include <cstring>

#include <string>
using namespace std;
int main()
{
    // These variables are unused.
    int q, s;
    char shea[] = "way";
    char gavin_stop_looking_at_ponies[] = "ay";

    // 'first' and 'last' are both names, not collections
    // of names.
    string first;
    string last;
    vector <string> first;
    vector <string> last;

    // 'endl' is unnecessary here; it outputs a newline and
    // flushes the stream, but standard output is usually
    // line-buffered, meaning that newline flushes the
    // stream regardless.
    cout << "Please enter your first name.\n"
    cout << "Please enter your first name." << endl;

    // If you just want to get one name, 'getline' is perfect.
    getline(cin, first);

    // This loop would run 0 times because 'first' is an
    // empty vector.
    for (int i = 0; i < first.size(); i++)
    {
        getline (cin, first[i]);
        string nfirst = first[i];
        while (nfirst[q])
        {
            nfirst[q] = tolower(nfirst[q]);
        }
        first[i] = nfirst;
    }

    // To make a string lowercase, use 'tolower' on each character.
    // Here's one way to do it:
    for (string::size_type i = 0; i < first.size(); ++i)
        first[i] = tolower(first[i]);

    // Here's another, with C++11 enabled:
    for (auto& c : first)
        c = tolower(c);

    cout << "Please enter your last name.\n";
    cout << "Please enter your last name." << endl;

    // Same thing.
    getline(cin, last);
    for (int j = 0; j < last.size(); j++)
    {
        getline (cin, last[j]);
        string nlast = last[j];
        while (nlast[s])
        {
            nlast[s] = tolower(nlast[s]);
        }
        last[j] = nlast;
    }

    // Now 'first' is a string, and 'first[0]' is a 'char'.
    // "a" is a string literal; 'a' is a character literal.
    // You can compare each character individually:
    if (first[0] == 'a' || first[0] == 'e' || first[0] == 'i' || first[0] == 'o' || first[0] == 'u')

    // Or you can say "if the character was found in this
    // set of vowels".
    if (string("aeiou").find(first[0]) != string::npos)

    if ( (first[0] == "a") ||( first [0] == "e") || (first [0] == "i") || (first [0] == "o")     || (first [0] == "u"))
    {
        // This would try to copy "way" into 'first':
        // formerly a vector of string objects, now just a
        // string object. 'strcpy' wants a character buffer,
        // and will overwrite characters in that buffer—
        // probably not what you want:
        //
        // "aaron" => "wayon"
        // 
        strcpy (first, "way");

        // Instead, just append "way":
        first += "way";
    }
    else
    {
        // This says "take the first first character of the
        // string, add the value of that character to a
        // pointer to a buffer containing "ay", then try to
        // copy the resulting pointer past the end of the
        // string. Again, not quite what you intended!
        first[first.size()] = first[0] + "ay";

        // Think of it instead like this: take everything
        // after the first character, add a string consisting
        // of the first character back onto the end, then add
        // "ay" after that.
        first = first.substr(1) + string(1, first[0]) + "ay";
    }

    // Duplicated code! You could move the above logic into
    // a function to avoid this duplication. Then you only
    // have to work on it in one place. :)
    if ( (last[0] == "a") ||( last [0] == "e") || (last [0] == "i") || (last [0] == "o") || (last [0] == "u"))
    {
        strcpy (last, "way");
    }
    else
    {
        last[last.size()] = last[0] + "ay";
    }

    // I need a space between my first and last names!
    cout << first << ' ' << last << '\n';
    cout << first << last << endl;
    return 0;
}

Upvotes: 5

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275370

first is a vector<string>, not a string. vector doesn't support << with cout. If you want to output each std::string in the std::vector, try iterating over the std::vector and outputting them one character at a time.

Similarly, first is a vector<string>, which cannot be implicitly converted to a char*. strcpy operates on raw blocks of char data, which first is far removed from. strcpy is for C-level operations on string buffers (and even then, is dangerous to use).

first[first.size()] = first[0] + "ay"; is undefined behavior, as you are accessing one-past-the-last-element of first, which is invalid memory. If you want to push something into the back of first, try first.push_back( first[0] + "ay" );.

Quite probably you are confused as to the difference between std::string, a char and a char*. These are completely different things. std::string is a managed buffer of char. char is a single 8 bit value, often used to store literals like 'a' (which is of type char). char* is a pointer to a single char, often used as a pointer to the start of an unmanaged buffer of char packed tightly.

Your std::vector<std::string> is a managed buffer of managed char buffers. Each element in the vector is a complete buffer some unknown number of chars, not a single char.

Upvotes: 1

Related Questions