Using getline on a string - c++

How can I use getline() to read words form a line that I have stored in a string?
For example:
char ch[100],w[20];
cin.getline(ch,100);
Now can I use getline to count the number of words in ch? I don't want to use a delimiter to directly count words from ch. I want to read words from ch and store them in w.
I have tried using ch in getline as a parameter.

getline is implemented in the standard as either a stream method, or a string method which takes a stream: http://en.cppreference.com/w/cpp/string/basic_string/getline
There is no standard implementation of getline which does not require a stream.
That said you can use ch to seed a istringstream to count the words in the string, but basic_istream<CharT, Traits>& getline(basic_istream<CharT, Traits>&& input, basic_string<CharT, Traits, Allocator>& str) assumes a newline as the delimiter so that's not what you're going to want to count words. Similarly, a getline that takes a delimiter will only break on a specific character. Instead you could use basic_istream& basic_istream::operator>> which will split words on all whitespace characters:
istringstream foo(ch);
for(auto i = 1; foo >> w; ++i) {
cout << i << ": " << w << endl;
}
Live Example
Just as a note here, defining char w[20] is just begging for an out of bounds write. At a minimum you need to define that such that if ch is filled with non-whitespace characters, w can contain it all. You could do that by defining char w[100].
But if someone were to come and increase the size of ch without changing the size of w and then you'd be in trouble again. In C++17 you could define w like this char w[size(ch)] prior to that you could do char w[sizeof(ch) / sizeof(ch[0])]
But your best option is probably to just make both w and ch strings so they can dynamically resize to accommodate user input.

Related

Switching from formatted to unformatted input in C++

I have an input text file. The first line has two int numbers a and b, and the second line is a string. I want to use formatted input to do file >> a >> b, and then unformatted input to get the characters of the string one by one. In between the two steps, I need to skip over the '\n' character at the end of the first line. I used
while(file.get()<=' ' && !file.eof()); // skip all unprintable chars
if(!file.eof()) file.unget(); // keep the eof sign once triggered
to make the input format more flexible. The user can now separate the numbers a and b from the string using an arbitrary number of empty lines '\n', tab keys '\t', and/or space keys ' ' -- the same freedom he has to separate the numbers a and b. There's even no problem reading in Linux a text file copied from Windows when every end of line now becomes "\r\n".
Is there an ifstream function that does the same thing (skip all chars <=' ' until the next printable char or EOF is reached)? The ignore function does not seem to do that.
Yes, there is: std::ws manipulator. It skips whitespace characters until a non-whitespace is found or end of stream is reached.. It is similar to use of whitespace character in scanf format string.
In fact, it is used by formatted input before actually starting to parse characters.
You can use it like that:
int x;
std::string str;
std::cin >> x >> std::ws;
std::getline(std::cin, str);
//...
//std::vector<int> vec;
for(auto& e: vec) {
std::cin >> e;
}
std::getline(std::cin >> std::ws, str);

How does char extraction differ from string extraction?

When disabling whitespace skipping with chars and strings the behavior is different. It seems the only way to extract an entire string (including whitespace characters) is to use chars and noskipws. But this is not possible with strings because it won't extract after the first space.
std::string test = "a b c";
char c;
std::istringstream iss(test);
iss.unsetf(std::ios_base::skipws);
while (iss >> c)
std::cout << c;
will output a b c but change c to string and it only outputs a.
The >> operator for a string extracts words, and stops at the
first white space it sees. If it doesn't skip initial white
space, then it stops immediately, and returns an empty string.
You don't say how you want the string to be delimited. To read
until the end of line, just use std::getline. To read until
the end of file, you can use something like:
std::istringstream collector;
collector << iss.rdbuf();
std::string results = collector.str();
It's not the most efficient, but if the file is small, it will
do.

Checking if a char is a newline

I'm doing an exercise in C++ Primer and basically I am using a switch statement to count the number of vowels in a text that I input.
I input the text using a while loop.
while(cin >> ch)
and proceed with the cases, a, e, i, o, u, incrementing an integer variable for the respective cases. Now the next part of the question says also count the spaces, tabs and newlines.
I tried doing
case ' ':
and so forth using '\t' and '\n'. But it seems like it doesn't compute these cases. I also tried just using a default and using an if else statement
default:
if(ch == ' ')
++space;
etc. But this doesn't proceed either. I also tried putting in the integer values of ' ', '\t', '\n'. What am I doing wrong here? Also, I know that if I use isspace() I can count the combined total but I need to compute each one individually. I'm not sure why the equality test won't do the job.
By default, formatted input from streams skips leading whitespace. You need to either disable skipping of leading whitespaces or use one of the functions which won't skip spaces:
std::cin >> std::noskipws; // disables skipping of leading whitespace
char c;
while (std::cin.get(c)) { // doesn't skip whitespace anyway
...
}
As said by Dietmar, white spaces are skipped by default. You could use cin.getline() to provide your own string delimiter instead of white space characters. I would say this is a generally easier way of reading input compared to using cin.get().

How can I get a word from string

I have f.e. "I am working as a nurse."
How Can I or which function use to get word from letter number 1 to space or to letter number 11?
So should I get " am working "
To read a word from a stream use operator>> on a string
std::stringstream stream("I am working as a nurse.");
std::string word;
stream >> word; // word now holds 'I'
stream >> word; // word now holds 'am'
stream >> word; // word now holds 'working'
// .. etc
It is not totally clear what you want, but from your example it seems like you want the substring that starts at character 1 and ends on the character 11 places later (that's 12 characters total). That means you want string::substr:
std::string str("I am working as a nurse");
std::string sub = str.substr(1,12);
char[] source = "I am working as a nurse."
char[11] dest;
strncpy(dest, &source[1], 10)

istream and cin.get()

I have a question about the difference between these two pieces of code:
char buffer5[5];
cin.get(buffer5, 5);
cout << buffer5;
cin.get(buffer5, 5);
cout << buffer5;
and
char buffer4;
while (cin.get(buffer4))
{
cout << buffer4;
}
In the first piece of code, the code gets 5 characters and puts it in buffer5. However, because you press enter, a newline character isn't put into the stream when calling get(), so the program will terminate and will not ask you for another round of 5 characters.
In the second piece of code, cin.get() waits for input to the input stream, so the loop doesn't just terminate (I think). Lets say I input "Apple" into the input stream. This will put 5 characters into the input stream, and the loop will print all characters to the output. However, unlike the first piece of code, it does not stop, even after two inputs as I can continuously keep inputting.
Why is it that I can continuously input character sequences into the terminal in the second piece of code and not the first?
First off, "pressing enter" has no special meaning to the IOStreams beyond entering a newline character (\n) into the input sequence (note, when using text streams the platform specific end of line sequences are transformed into a single newline character). When entering data on a console, the data is normally line buffered by the console and only forwarded to the program when pressing enter (typically this can be turned off but the details of this are platform specific and irrelevant to this question anyway).
With this out of the way lets turn our attention to the behavior of s.get(buffer, n) for an std::istream s and a pointer to an array of at least n characters buffer. The description of what this does is quite trivial: it calls s.get(buffer, n, s.widen('\n')). Since we are talking about std::istream and you probably haven't changed the std::locale we can assume that s.widen('\n') just returns '\n', i.e., the call is equivalent to s.get(buffer, n, '\n') where '\n' is called a delimiter and the question becomes what this function does.
Well, this function extracts up to m = 0 < n? n - 1: 0 characters, stopping when either m is reached or when the next character is identical to the delimiter which is left in the stream (you'd used std::istream::getline() if you'd wanted the delimiter to be extracted). Any extracted character is stored in the corresponding location of buffer and if 0 < n a null character is stored into location buffer[n - 1]. In case, if no character is extracted std::ios_base::failbit is set.
OK, with this we should have all ingredients to the riddle in place: When you entered at least one character but less than 5 characters the first call to get() succeeded and left the newline character as next character in the buffer. The next attempt to get() more characters immediately found the delimiter, stored no character, and indicated failure by setting std::ios_base::failbit. It is easy to verify this theory:
#include <iostream>
int main()
{
char buffer[5];
for (int count(0); std::cin; ++count) {
if (std::cin.get(buffer, 5)) {
std::cout << "get[" << count << "]='" << buffer << "'\n";
}
else {
std::cout << "get[" << count << "] failed\n";
}
}
}
If you enter no character, the first call to std::cin.get() fails. If you enter 1 to 4 characters, the first call succeeds but the second one fails. If you enter more than 4 characters, the second call also succeeds, etc. There are several ways to deal with the potentially stuck newline character:
Just use std::istream::getline() which behaves the same as std::istream::get() but also extracts the delimiter if this is why it stopped reading. This may chop one line into multiple reads, however, which may or may not be desired.
To avoid the limitation of a fixed line length, you could use std::getline() together with an std::string (i.e., std::getline(std::cin, string)).
After a successful get() you could check if the next character is a newline using std::istream::peek() and std::istream::ignore() it when necessary.
Which of these approaches meets your needs depends on what you are trying to achieve.