This question already has answers here:
Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
(5 answers)
Closed 9 years ago.
I have two questions. I'm trying to read in line by line from a file but I can't get each line to go individually to where I can use it. Also, I can't figure out how to ask the user for their file name and than use what they type in the program. All the examples I've found just has the name of the file in the code already and just putting it in cin doesn't seem to work. I am trying to separate two different types of lines, so for example, abcd and 1234. If the first value is a letter do case a, if it's a number, do case b. But all I've managed to do is have getline have everything in one so I can't actually separate it. Anyone have any advise?
string x;
cout << "Enter your file: " ;
cin >> x
string line;
ifstream myfile;
myfile.open (x);
while(!myfile.eof())
{
getline(myfile,line, ' ');
}
cout << line << endl;
There is nothing wrong with your cin statement for reading the file name. As long as the file exists, what you have will open the file. Though, you can add some error checking to it:
std::string x;
std::cout << "Enter your file: " ;
if (!(std::cin >> x))
{
std::cerr << "Invalid input!" << std::endl;
return -1;
}
std::ifstream myfile(x);
if (myfile.is_open())
{
std::string line;
while (myfile >> line)
{
std::cout << line << std::endl;
}
}
else
{
std::cerr << "Unable to open file: " << x << std::endl;
return -1;
}
Note the proper while condition (do not use eof() for std::istream while conditions!). Additionally, if you are separating on whitespace, there is no need to use std::getline - operator>> will do the same thing.
If you want to do different things based on what the value of line is, then check the string. For example:
if (line[0] >= '0' && line[0] <= '9')
// do something with digits
else
// do something with non-digits
First, don't put eof() in while condition. It's wrong because iostream::eof will only be set after reading the end of the stream. It does not indicate, that the next read will be the end of the stream. Check out this post: Why is iostream::eof inside a loop condition considered wrong?
To separate, you can check the first char of the line is whether within ['0', '9'] or not.
Like this:
while( getline(myfile, line) )
{
if (line[0]>='0' && line[0]<='9')
{
// start with a number (do case b)
}
else
{
// other (do case a)
}
}
Related
In the following code I can't use the std::string class.
I'm trying to read a file that has empty lines, and I want to ignore the empty lines when I encounter them.
I've tested out some ideas using a simplified example and a made-up text file containing empty lines.
int main() {
ifstream fin;
fin.open("test.txt");
if(fin.fail()){
cout << "Input file fail.";
exit(-1);
}
char line[10];
while(!fin.eof()){
fin.getline(line, 10);
if(line[0] == '\n')
cout << "skip" << "\n";
else
cout << line << "\n";
}
}
I've also tried things like strlen(line) == 1, but nothing worked so far.
What is the problem?
std::getline() already takes care of the '\n' character (it will be discarded), line[0] would contain '\0' in the case of an empty input.
Just compare for '\0' instead of '\n':
if(line[0] == '\0')
cout << "skip" << "\n";
else
cout << line << "\n";
I've also tried things like strlen(line) == 1
If so, shouldn't that have been strlen(line) == 0 for an empty line?
There are two problems with your code. The first is that your loop isn't correct. You have to check the result of the getline() call, not eof(). Please see Why is iostream::eof inside a loop condition considered wrong. That restructured loop becomes:
while (fin.getline(line, 10)) {
...
}
Next, when you read a line with getline() (and 10 seems really short for a line), the delimiter will not be part of the body. There will not be a \n in line when you're doing reading. You'll just have an empty string. So the right way to discard empty lines is:
while (fin.getline(line, 10)) {
if (line[0]) {
// we have contents!
}
}
Or really, std::string is just way better:
std::string line;
while (std::getline(fin, line)) {
if (!line.empty()) {
....
}
}
std::getline() discards the delimiter \n. But you might also want to consider that on some systems lines may be delimited by \r\n.
After inputting getline, the first row of numbers in my input file are ignored and the program starts at the second line until the end.
Here's the code:
while (!file.eof())
{
getline(file, lineBuffer);
if(lineBuffer.length()==0)
{
continue; //ignore empty lines
}
else
{
// file.open("test.txt");
while (file >> A >> B >> N)
{
for(int count=1; count<=N; count++)
{
if(count%A == 0 && count%B == 0) { cout << "FB "; }
else if(count%A == 0) { cout << "F "; }
else if(count%B == 0) { cout << "B "; }
else { cout << count << " "; }
}
cout << endl;
}
}
}
The input file contains 3 rows of integers and only the second line and third like are processed. I've looked this up extensively, but no one has mentioned a case similar to mine. Usually they deal with people trying to get user input after a getline function. Any ideas would be greatly appreciated.
getline(file, lineBuffer) reads a line from file and stores it in lineBuffer. If you want to use it, you should then process lineBuffer instead of reading from file (where the first line will already be skipped). To make it work, simply remove all code outside of the inner while. This will read from the file 3 numbers at a time, stopping when extracting one of them fails.
Is the above code real or is it an example?
If it is real, you don't need to skip th eempty lines. The operator>> will skip the whitespaces for you.
The following code without any lineskipping performs just the same reading of "A B N" lines:
// outer loop is not needed, either remove it, or include error checking
// while (!file.eof() && !file.fail())
{
while (file >> A >> B >> N)
{
for(int count=1; count<=N; count++)
{
if(count%A == 0 && count%B == 0) { cout << "FB "; }
....
If for some reason you cannot remove the getline at all, remember that getline reads the line ALWAYS (well, unless the streampointer reached its end). This means that your code skips the empty lines, then reads first nonempty line, notices that the line is not empty and breaks the loop. Note that it has read the line, hence, your first-line-of-data now is in the linebuffer, and not in the stream anymore. Now you have to either unget the whole line (if your stream supports it) or rewind the stream by the line's length (if your stream supports it) or - just read the data from the line - for example using istringstream class.
In general, mixing getline and operator>> is doable but tricky, (especially when you first read via >> and then want to skip a 1 or 2 lines), as they behave a little differently in terms of buffering and whitespace-handling. If you really want that, search for this subject - there are lots of examples how to sync them.
How do I run the while loop until the end of line or null character reached.
Here is my code
#include<iostream>
using namespace std;
main()
{
char input[20];
cout<<"Enter a line: ";
cin>>input;
while(input!='\0')
{
cout<<"This is a text";
}
system("pause");
}
If you want to read until either a newline or a NUL, read one character at a time inside the loop.
#include<iostream>
int main()
{
char input;
std::cout << "Enter a line: " << std::flush;
while(std::cin >> input && input != '\n' && input != 0) {
std::cout << "This is a test\n";
}
}
Notes:
main requires a return type
Never, ever, say "using namespace std;"
Don't forget to flush if you want cout to appear immediately.
Notice the compound test in the while condition:
First, did the read succeed?
Next, is it not '\n' (one of your conditions).
Next, is it not NUL (the other of your conditions).
The body of the loop will be executed once per input character -- is that what you wanted?
But, consider if you have correctly specified your requirement. It is an unusual requirement -- why would there be a NUL in a text file, and why would you want to process each character individually?
In idiomatic C++, you can read the input file in a line at a time using std::getline:
std::string myString;
while(std::getline(std::cin, myString)) {
// process myString
}
If you just want to read in a single line:
std::string myString;
if(std::getline(std::cin, myString)) {
// process myString
}
Finally, if you want to read a line, and ignore its contents:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
try something like:
i = 0;
while ((input[i] != '\0') && i < 20)
{
cout<<"This is a text";
i++;
}
Like this:
std::string line;
if (std::getline(std::cin, line))
{
std::cout << "Thank you, you said, \"" << line << "\".\n";
}
else
{
// error, e.g. EOF
}
If you want to read multiple lines, use a while loop instead:
while (std::getline(std::cin, line))
{
std::cout << "Thank you, you said, \"" << line << "\".\n";
}
The issue is that you're reading an entire chunk of text at once and then printing it until the input is '\0'. However, you're never actually updating this inside the loop. You can either use cin inside the loop to get the input, OR if you're trying to output each character, you can index the char array.
I wrote the code below that successfully gets a random line from a file; however, I need to be able to modify one of the lines, so I need to be able to get the line character by character.
How can I change my code to do this?
Use std::istream::get instead of std::getline. Just read your string character by character until you reach \n, EOF or other errors. I also recommend you read the full std::istream reference.
Good luck with your homework!
UPDATE:
OK, I don't think an example will hurt. Here is how I'd do it if I were you:
#include <string>
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
static std::string
answer (const string & question)
{
std::string answer;
const string filename = "answerfile.txt";
ifstream file (filename.c_str ());
if (!file)
{
cerr << "Can't open '" << filename << "' file.\n";
exit (1);
}
for (int i = 0, r = rand () % 5; i <= r; ++i)
{
answer.clear ();
char c;
while (file.get (c).good () && c != '\n')
{
if (c == 'i') c = 'I'; // Replace character? :)
answer.append (1, c);
}
}
return answer;
}
int
main ()
{
srand (time (NULL));
string question;
cout << "Please enter a question: " << flush;
cin >> question;
cout << answer (question) << endl;
}
... the only thing is that I have no idea why do you need to read string char by char in order to modify it. You can modify std::string object, which is even easier. Let's say you want to replace "I think" with "what if"? You might be better off reading more about
std::string and using find, erase, replace etc.
UPDATE 2:
What happens with your latest code is simply this - you open a file, then you get its content character by character until you reach newline (\n). So in either case you will end up reading the first line and then your do-while loop will terminate. If you look into my example, I did while loop that reads line until \n inside a for loop. So that is basically what you should do - repeat your do-while loop for as many times as many lines you want/can get from that file. For example, something like this will read you two lines:
for (int i = 1; i <= 2; ++i)
{
do
{
answerfile.get (answer);
cout << answer << " (from line " << i << ")\n";
}
while (answer != '\n');
}
I am trying to read a poorly formatted text file, and maybe I'm going about this the wrong way, but based on the getline documentation it sounded like it would pull values until the values weren't a delimiter value (' ', in my case):
"If the delimiter is found, it is extracted and discarded, i.e. it is
not stored and the next input operation will begin after it. If you
don't want this character to be extracted, you can use member get
instead."
But for some reason it returns nothing many times. See lines 604-607, all those commas in my output are returns of getline. Could someone tell me why it is returning blanks 6 times before it comes to the value? The text file only contains one space before the value. Thanks in advance. :)
Relevant screenshot: http://j.drhu.me/2011-09-07_1317.png
#include <iostream>
#include <fstream>
#include <string>
void CMuscleModel::LoadOpParams()
{
int i, j;
ifstream param("params.txt", ios::in);
if (param.is_open())
{
stringstream iss, isn;
string line, word;
i=0; j=0;
while (getline(param,line))
{
isn.clear();
isn << line;
if(i>27){
while (getline(isn,word,' ')) {
//LGma[i][j]=atof(word.c_str());
if(word == "SM"){
getline(param,line);
cout << line << endl << endl;
isn.clear(); isn << line;
getline(isn,word,' ');
int junk=0;
while (atof(word.c_str())==0){
junk++;
getline(isn,word,' ');
}
cout << atof(word.c_str()) << ", " << junk << endl;
}
if(word == "ST"){
cout << word << endl;
}
if(word == "BFL"){
cout << word << endl;
}
if(word == "BFS"){
cout << word << endl;
}
if(word == "MG"){
cout << word << endl;
}
if(word == "LG"){
cout << word << endl;
}
if(word == "RF"){
cout << word << endl;
}
if(word == "VM"){
cout << word << endl;
}
if(word == "VL"){
cout << word << endl;
}
if(word == "VI"){
cout << word << endl;
}
j++;
}
}
j=0; i++;
isn.clear();
}
}
param.close();
}
Ah, sorry for not including code.
If you're using space as a delimiter anytime it's encountered getline will return with whatever there was upto the delimiter. If the file had 5 spaces in a row before any other characters for example you'd now have to call getline 6 times.
Perhaps use the default newline character instead '\n'?
Edit: Didn't see code before. Perhaps restructure your code to read lines and then use find in conjunction with substr on each line to search for your keywords? Would be simpler code and less looping. There is no reason to read from the file only to output to a stringstream which you then read from.
Bi-directional I/O with std::stringstream is really ambiguous. I recommand that you use it a little differently.
ifstream param("params.txt", ios::in);
if (param.is_open())
{
stringstream iss;
string line, word;
i=0; j=0;
while (getline(param,line))
{
istringstream isn(line);
// ...
}
}
This creates a fresh string stream with clean state and contains the contents of the line read from the file each time. If you really want to re-use the instance for reading tokens on multiple lines, I recommand you use the .str(line) syntax rather than .clear() and operator<<.
If you want to clear the whitespace at the beginning of each line, you can use the std::ws manipulator:
istringstream isn(line);
isn >> ws;
// ...
I think the output text file I was reading from had trailing spaces and they were just being put into the stream so I was really confused about what was going on. I simply used .str("") at the end of each line to reset my current stream and things worked out marvelously. Thanks for all the help guys.