Vectors & Paragraph Iteration

Hi everybody,

I have a question that I am trying to work through. I have just started learning iteration and have encountered a problem.

The question asks to get an input from a user that amounts to a paragraph, store it in a vector, then capitalise all the text in the first paragraph only and output it.

First things first: when I enter in a paragraph, double linespace and another paragraph, the vector concatenates both paragraphs in to one. How do I stop that so that I output two paragraphs but only the first one capitalised.

Second question: How do I capitalise using an iterator. I can do it fine using substring, but iterator gives me an error: "No matching function for call toupper".

Here is my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>
#include <vector>

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

int main ()
{
    vector<string> text;
    string words;
    
    while (cin >> words) {
        text.push_back(words);
    }
    for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
        *it = toupper(*it)
        cout << *it << " ";
}


Thanks in advance!
Hi there,

Regarding the second question: try using a non-const iterator in your for loop: text.begin().
Const iterators will not allow you to change the value.

I would advise against reading the paragraph in word for word.
You could use std::getline(std::cin, words) to read one paragraph into a string.
If it's an empty string, stop asking for more input.

That way you have both paragraphs separated from the get go.

Please do let us know if you require any further help.

All the best,
NwN
Hi NwN,

Sorry for replying late, my internet has been down.

I tried removing the constant 'declarer' (good point there), but it still actually gives the same error? I thought for sure that would fix it.

I'll give the getline suggestion a go and see what happens. Not too familiar with the getline, but it is always good to try new things out.

Thanks!
The problem is using text.cbegin/end() the c on the front stands for const. Just use normal begin/end.

Also, your indentation is incorrect on the for loop. This is what it will actually be:
1
2
3
for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
    *it = toupper(*it)
cout << *it << " "; //note that this line is not in the loop 
*it is of type std::string. std::toupper works on characters, not strings.

One way to do it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>
#include <vector>

std::string asUpper(const std::string & s)
{
    std::string result ;
    std::transform(s.cbegin(), s.cend(), std::back_inserter<std::string>(result), std::toupper) ; 
    return result ;
}

int main()
{
    std::vector<std::string> paragraphs ;

    while ( std::cin )
    {
        std::string paragraph ;
        std::string input ;

        while ( getline(std::cin, input) && input.length() )
            paragraph += (input + '\n') ;

        if ( paragraph.length() )
            paragraphs.emplace_back(paragraph) ;
    }

    if ( paragraphs.size() )
    {
        auto it = paragraphs.begin() ;
        std::cout << asUpper(*it) << '\n' ;
        while ( ++it != paragraphs.end() )
            std::cout << *it << '\n' ;
    }
    else
        std::cout << "No text!\n" ;
}
Last edited on
@firedraco Thanks for that, I didn't think about the indentation thing. Also, I removed the begin/cend thing and put it with normal begin/end but I still get the toupper issue.

@cire Good point... in the example the book gives on the previous page of iterating through a string they are actually using String s ("Some String") and then iterating through that... now it calls for a vector and hasn't explained that part yet.

Although I would love to try out your code, it covers stuff that I haven't done yet, so no idea what it is doing honestly. I need to find a way using only begin/end, *it, toupper, vectors and the input. Not sure how though given your point about the characters not strings...
In my code, asUpper creates a new uppercase string from the parameter and returns it. It could've been written:

1
2
3
4
5
6
7
8
std::string asUpper(const std::string& s)
{
    // for each character in s, add the uppercase version to result.
    std::string result ;
    for ( auto it = s.begin(); it != s.end(); ++it )
        result += std::toupper(*it) ;
    return result ;
}


Now, in main, the paragraphs variable is meant to store, in each element, a paragraph. It isn't a collection of individual words, or individual lines. Each element will be a full paragraph.

The while (std::cin) should be fairly obvious. We're going until input fails and puts std::cin into an error state, just like your original code. input is where we store each individual line as it is extracted from the input stream. paragraph is the paragraph we build with the individual lines. This could be done without the paragraph variable, but I thought this would be more clear.

That brings us to:
1
2
        while ( std::getline(std::cin, input) && input.length() )
            paragraph += (input + '\n') ;


std::getline extracts a line of text delimited by a newline and discards the newline. Like the extraction operator(>>), std::getline returns a reference to the std::getline returns a reference to the std::istream that is fed to it and reflects it's error state which is what we're checking in the while condition - while the input succeeded and the input didn't consist solely of a newline.

So if the input is "This is a line of text.\nAnd this is another one.\n\n" the first string extracted from the input stream is: "This is a line of text." Since the newline is discarded, it is added back to the input variable to preserve the original formatting and then the resulting string is appended to the paragraph variable. The second extraction yields the string "And this is another one." And the third extraction yields an empty string, because all that's left in the input stream is "\n", which breaks us out of the inner while loop.

Now, the paragraph string holds an entire paragraph, so we simply add it to the vector of paragraphs, and the process begins again at the top of the loop for any additional input remaining in the input stream.

Finally, paragraphs is iterated through. asUpper is used to output the first paragraph as an uppercase string. A newline is inserted into the output stream after each paragraph to maintain the original formatting, because the empty lines were discarded when input was processed.
Wow! Fantastic! Thanks so much for taking the time to write such a detailed explanation! It has really helped and I now understand exactly what you were doing... I learn something new everyday!

Thanks so much!

Could I just ask one really really simple thing quickly?... what constitutes an empty element in a string vector?
Last edited on
Although I have certainly learnt something new that will really help, I think I identified the problem in the question which is what the book wants me to solve.

It highlights that one cannot use a range for loop to change the length of a vector, it will be invalid, which is what I was trying to do in my code at the top. I have been trying to work through it but I'm still struggling...

The book says the following just before the question (I didn't read it properly the first time):

"In [exercise]3.3.2 (p. 101) we noted that there are implications of the fact that vectors can grow dynamically. We also noted that one such implication is that we cannot add elements to a vector inside a range for loop. Another implication is that any operation, such as push_back, that changes the size of a vector potentially invalidates all iterators into that vector. We'll explore how iterators become invalid in more detail [later]." - C++ Primer, 5th Edition

Then the question:

"Revise the loop that printed the first paragraph in text to instead change the elements in text that correspond to the first paragraph to all uppercase. After you've updated text, print its contents."

So I then did this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>
#include <vector>

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

int main ()
{
    string para;
    vector<string> text {"first paragraph of text bla bla bla"};
    
    while (cin >> para) {
        text.push_back(para);
    }
    
    for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
        cout << *it << endl;
}


This code is the code I used for the "...printed the first paragraph in text..." part of the question. This works just fine... because the for loop is doing nothing to the vector size. But as soon as I add a toupper call then it has a problem.

So, although the code provided by cire works great, I would like to solve this question the way the book is asking me to do it.

As always, all inputs are greatly appreciated!
Topic archived. No new replies allowed.