Reading two line continuously using getline function in c++
std::istream::getline
is being overloaded with data.
According to cppreference,
Behaves as UnformattedInputFunction. After constructing and checking the sentry object, extracts characters from *this and stores them in successive locations of the array whose first element is pointed to by s, until any of the following occurs (tested in the order shown):
- end of file condition occurs in the input sequence (in which case
setstate(eofbit)
is executed)- the next available character c is the delimiter, as determined by
Traits::eq(c, delim)
. The delimiter is extracted (unlike basic_istream::get()) and counted towards gcount(), but is not stored.count-1
characters have been extracted (in which casesetstate(failbit)
is executed).
Emphasis mine.
cin.getline(line1,7);
// ^ This is count
can read only 6 characters with the 7th reserved for the null terminator. "oranges" is seven characters, and this places cin
in a non-readable error state that must be cleared before reading can be continued. Reading of the second line
cin.getline(line2,7);
instantly fails and no data is read.
The obvious solution is
cin.getline(line1, sizeof(line1));
to take advantage of the whole array. But...
Any IO transaction should be tested for success, so
if (cin.getline(line1, sizeof(line1)))
{
// continue gathering
}
else
{
// handle error
}
is a better option.
Better still would be to use std::getline
and std::string
to almost eliminate the size constraints.
Is using getline() in a while loop bad practice?
There is no problem calling getline
in a loop as you do. As a matter of fact, this exactly the intended use case and used in the example in the man page.
realloc
is only called if the array allocated so far is too short for the current line, thus it will be called very little and you can even lower the number of calls by allocating an initial array and set its length in the len
variable.
Since getline
returns the number of characters read, you could write the line using fwrite
instead of fputs
. Note however that the behavior is subtly different: fwrite
will output lines read from the file containing embedded null bytes, whereas fputs
would stop on the first null byte. This is usually not an issue as text files usually do not contain null bytes.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Unable to open file %s: %s\n",
"file.txt", strerror(errno));
return 1;
}
char *buffer = NULL;
size_t len = 0;
ssize_t characters;
#if 1
// optional code to show how to start with a preallocated buffer
if ((buffer = malloc(256)) != NULL)
len = 256;
#endif
while ((characters = getline(&buffer, &len, fp)) != -1) {
fwrite(buffer, 1, characters, stdout);
}
fclose(fp);
free(buffer);
return 0;
}
Using getline along with dynamic storage
Since the goal is "print the contents in reverse order of the user's input, last line first", the program must store all the lines. The getline()
function typically allocates quite a large space for each line (128 bytes by default on my Mac, and growing if input lines are longer than that), so it is usually best to have a buffer managed by getline()
that can grow if need be, and to copy the actual input strings somewhere else with the requisite length. I use strdup()
to copy the line.
/* Read file and print the lines in reverse order */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char **ptrs = 0;
size_t numptrs = 0;
size_t count = 0;
char *buffer = 0;
size_t buflen = 0;
while (getline(&buffer, &buflen, stdin) != -1)
{
if (count == numptrs)
{
size_t newnum = (numptrs + 2) * 2;
void *newptrs = realloc(ptrs, newnum * sizeof(*ptrs));
if (newptrs == 0)
{
fprintf(stderr, "Out of memory (%zu bytes requested)\n", newnum * sizeof(*ptrs));
exit(1);
}
ptrs = newptrs;
numptrs = newnum;
}
ptrs[count++] = strdup(buffer);
}
free(buffer);
/* Print lines in reverse order */
for (size_t i = count; i > 0; i--)
fputs(ptrs[i-1], stdout);
/* Free allocated memory */
for (size_t i = 0; i < count; i++)
free(ptrs[i]);
free(ptrs);
return 0;
}
It's easy to argue that the code should check that strdup()
succeeds and take appropriate action if it does not. The code freeing the allocated memory should be in a function — that would make clean up after an error easier, too. The code could be revised into a function that could be used to process files listed as command-line arguments instead of standard input.
Given this text as standard input:
Because it messes up the order in which people normally read text.
> Why is top-posting such a bad thing?
>> Top-posting.
>>> What is the most annoying thing in e-mail?
the program produces the output:
>>> What is the most annoying thing in e-mail?
>> Top-posting.
> Why is top-posting such a bad thing?
Because it messes up the order in which people normally read text.
getline() in C creating an infinite loop and skipping first word?
but it's just creating an infinite loop.
This line of your code: while ((read = getline(&line, &len, list)) != 1)
Unless a line contains 1
character (just a newline), it will be an infinite loop. The POSIX getline()
function will return -1
(not EOF
, even though EOF
is usually -1) when the file is completely read. So change that line to:
while ((read = getline(&line, &len, list)) != -1)
But, I don't see you using the value of read
inside that loop, so this would be better:
- Fix 1:
while (getline(&line, &len, list) != -1)
And inside that loop, I see: printf("%d. %s", item, line);
You might find very old implementations of getline()
that don't include the newline, in which case, if you want your output in separate lines, you need to put a \n
:
- Fix 2:
printf("%d. %s\n", item, line);
However, if you use a more modern implementation, it will preserve the newline in accordance with the POSIX specification.
Also, if the very last 'line' in the file is not terminated with a newline, you might still want to add one. In that case, you could keep the read length and use that to detect whether there is a newline at the end of the line.
Also it seems to be skipping the first word for some reason
Because of int letterCount = fscanf(list,"%s",chars);
That fscanf
reads the first word of your file. Now the file pointer is at that position (end of the first word) and further reading of the file will happen from that place.
So, reposition the file pointer to the beginning of the file after reading the first word from the file:
Fix 3:
int letterCount = fscanf(list,"%s",chars);
fseek(list, 0, SEEK_SET); // <-- this will reposition the file pointer as required
Using getline(cin, s) after cin
cout << "Enter the number: ";
int number;
if (cin >> number)
{
// throw away the rest of the line
char c;
while (cin.get(c) && c != '\n')
if (!std::isspace(c))
{
std::cerr << "ERROR unexpected character '" << c << "' found\n";
exit(EXIT_FAILURE);
}
cout << "Enter names: ";
string name;
// keep getting lines until EOF (or "bad" e.g. error reading redirected file)...
while (getline(cin, name))
...use name...
}
else
{
std::cerr << "ERROR reading number\n";
exit(EXIT_FAILURE);
}
In the code above, this bit...
char c;
while (cin.get(c) && c != '\n')
if (!std::isspace(c))
{
std::cerr << "ERROR unexpected character '" << c << "' found\n";
exit(EXIT_FAILURE);
}
...checks the rest of the input line after the number contains only whitespace.
Why not just use ignore?
That's pretty verbose, so using ignore
on the stream after >> x
is an oft-recommended alternative way to discard content through to the next newline, but it risks throwing away non-whitespace content and in doing so, overlooking corrupt data in the file. You may or may not care, depending on whether the file's content's trusted, how important it is to avoid processing corrupt data etc..
So when would you use clear and ignore?
So, std::cin.clear()
(and std::cin.ignore()
) isn't necessary for this, but is useful for removing error state. For example, if you want to give the user many chances to enter a valid number.
int x;
while (std::cout << "Enter a number: " &&
!(std::cin >> x))
{
if (std::cin.eof())
{
std::cerr << "ERROR unexpected EOF\n";
exit(EXIT_FAILURE);
}
std::cin.clear(); // clear bad/fail/eof flags
// have to ignore non-numeric character that caused cin >> x to
// fail or there's no chance of it working next time; for "cin" it's
// common to remove the entire suspect line and re-prompt the user for
// input.
std::cin.ignore(std::numeric_limits<std::streamsize>::max());
}
Can't it be simpler with skipws or similar?
Another simple but half-baked alternative to ignore
for your original requirement is using std::skipws
to skip any amount of whitespace before reading lines...
if (std::cin >> number >> std::skipws)
{
while (getline(std::cin, name))
...
...but if it gets input like "1E6" (e.g. some scientist trying to input 1,000,000 but C++ only supports that notation for floating point numbers) won't accept that, you'd end up with number
set to 1
, and E6
read as the first value of name
. Separately, if you had a valid number followed by one or more blank lines, those lines would be silently ignored.
Taking user input with getline() and comparing to another string in a while loop
Replace
while(buffer != "exit");
with
while(strcmpi(buffer, "exit"));
In C, you can't check logical equality of string with == or != operators. You need to use functions like 'strcmp
' or strcmpi
.
Using getline() in C++
If you're using getline()
after cin >> something
, you need to flush the newline character out of the buffer in between. You can do it by using cin.ignore()
.
It would be something like this:
string messageVar;
cout << "Type your message: ";
cin.ignore();
getline(cin, messageVar);
This happens because the >>
operator leaves a newline \n
character in the input buffer. This may become a problem when you do unformatted input, like getline()
, which reads input until a newline character is found. This happening, it will stop reading immediately, because of that \n
that was left hanging there in your previous operation.
Related Topics
C/C++ Int[] VS Int* (Pointers Vs. Array Notation). What Is the Difference
How to Enable Gdb Pretty Printing For C++ Stl Objects in Eclipse Cdt
Why Function Template Cannot Be Partially Specialized
Is Sizeof in C++ Evaluated At Compilation Time or Run Time
Deleting a Pointer to Const (T Const*)
Generate Random Double Numbers in C++
Compiling Multithread Code With G++
Why Compiler Doesn't Allow Std::String Inside Union
C++ Printing Boolean, What Is Displayed
How to Compile C Code With Anonymous Structs/Unions
Why Doesn't a Derived Template Class Have Access to a Base Template Class' Identifiers
Finding All the Subsets of a Set