I'm currently trying to write a loop to keep extracting each line from an input file using getline, and keep running until it detects the end of a paragraph.
I found this as a reference:
while (getline(inFile, line))
{
if (line.empty())
break;
else
{
for (int j = 0; j < line.length(); j++)
{
//Doing stuff here
}
}
}
I've tested this and it runs fine. However, I'm not allowed to use break statements for my assignment.
I thought I could do something like this:
while (getline(inFile, line))
{
while (!line.empty())
{
for (unsigned int i = 0; i < line.length(); i++)
{
//Do stuff
}
}
}
But the program ends up crashes when the loop iterates for the first time. I was wondering if anyone had some input how I could resolve this issue
This is how you solve it:
while (getline(inFile, line) && !line.empty()) { ... }
it has the exact same effect and it arguably improves the readability. The problem with your approach is that it causes an infinite loop. The fact that line.empty() evaluates false does not effect the outer while there.
The following also demonstrates the use of a stringstream for testing. exec contains 3 tests snippets.
int exec(int , char** )
{
int retVal = 0;
cout << "\n\n";
string s =
" now is the time\n"
" for all good men\n"
"\n" // empty line
" to come to the aid \n"; // eof()
// solution 1 has infinite loop
if(0) // disable
{
stringstream ss;
ss << s; // load ss
string line;
while (getline(ss, line))
{
while (!line.empty())
{
for (uint j=0; j < line.length(); ++j)
cout << line[j];
}
cout << endl;
}
}
// solution 2 exits too soon, line 3 is empty, line 4 dropped
{
cout << "\n\n";
stringstream ss;
ss << s; // load ss
string line;
while (getline(ss, line) && !line.empty())
{
cout << line << endl;
}
}
// output:
// now is the time
// for all good men
// solution 3 - small test effort, but seems to work
{
cout << "\n\n";
stringstream ss;
ss << s; // load ss
do
{
string line;
getline(ss, line); // read a line
if (!line.empty()) // test it has content
cout << line << endl; // use when non-empty
} while(!ss.eof()); // how continue when file has more lines
} // solution 3 seems to work
// output:
// now is the time
// for all good men
// to come to the aid
return retVal;
}
Related
Please can you advise, why the inner loop runs only once?
I'd like to add suffix to each line of input file and then store the result in output file.
thanks
For example:
Input file contains:
AA
AB
AC
Suffix file contains:
_1
_2
Output file should contain:
AA_1
AB_1
AC_1
AA_2
AB_2
AC_2
My result is :
AA_1
AB_1
AC_1
Code:
int main()
{
string line_in{};
string line_suf{};
string line_out{};
ifstream inFile{};
ofstream outFile{"outfile.txt"};
ifstream suffix{};
inFile.open("combined_test.txt");
suffix.open("suffixes.txt");
if (!inFile.is_open() && !suffix.is_open()) {
perror("Error open");
exit(EXIT_FAILURE);
}
while (getline(suffix, line_suf)) {
while (getline(inFile, line_in))
{
line_out = line_in + line_suf;
outFile << line_out << endl;
}
inFile.close();
outFile.close();
}
}
IMHO, a better method is to read the files into vectors, then iterate through the vectors:
std::ifstream word_base_file("combined_test.txt");
std::ifstream suffix_file("suffixes.txt");
//...
std::vector<string> words;
std::vector<string> suffixes;
std::string text;
while (std::getline(word_base_file, text))
{
words.push_back(text);
}
while (std::getline(suffix_file, text))
{
suffixes.push_back(text);
}
//...
const unsigned int quantity_words(words.size());
const unsigned int quantity_suffixes(suffixes.size());
for (unsigned int i = 0u; i < quantity_words; ++i)
{
for (unsigned int j = 0; j < quantity_suffixes; ++j)
{
std::cout << words[i] << suffix[j] << "\n";
}
}
Edit 1: no vectors
If you haven't learned about vectors or like to thrash your storage device you could try this:
std::string word_base;
while (std::getline(inFile, word_base))
{
std::string suffix_text;
while (std::getline(suffixes, suffix_text))
{
std::cout << word_base << suffix_text << "\n";
}
suffixes.clear(); // Clear the EOF condition
suffixes.seekg(0); // Seek to the start of the file (rewind).
}
Remember, after the inner while loop, the suffixes file is at the end; no more reads can occur. Thus the file needs to be positioned at the start before reading. Also, the EOF state needs to be cleared before reading.
I made a quiz program in c++. It's working but with separate blocks of instructions for every question in file. I need to transform the block of instructions(after for) to work for all questions.
The file look like this
1.WHEN COMPUTER WAS FIRST INVENTIONED?
a.1822
b.1823
c.1834
d.1922
2.WHO kILLED PRESEDENT BENOGIR VUTTO?
a.nawaz shrif
b.pervase
c.non of them
d.political leder
This is only a function in my program.
void Question::quiz(int &Total)
{
string line[200];
string answer;
string easy[15]={"a","c","a","a","b","c","d","c","a","b","b","c","c","c","a"};
ifstream fin("questions.txt");
if(!fin)
{
cout<<"Cannot open file\n";
exit(1);
}
cout<<"The first question is\n";
for(int contor=0;contor<5;contor++)
{
getline(fin,line[contor]);
cout<<line[contor]<<'\n';
}
cout<<"Select your answer: ";
cin >> answer;
if(answer==easy[0])
{
Total+=1;
}
cin.get();
}
You can use a while loop for reading the file upto end of line. As every block contains exactly five lines, you can take input for each line until you get the line size greater than 0. As blank line will also be in the input and you need to ignore them.
void Question::quiz(int &Total)
{
string line[200];
string answer;
string easy[15]= {"a","c","a","a","b","c","d","c","a","b","b","c","c","c","a"};
ifstream fin("questions.txt");
if(!fin)
{
cout<<"Cannot open file\n";
exit(1);
}
int cnt=0;
while(getline(fin,line[0]))
{
cout<<line[0]<<endl;
while(line[0].size()==0)
{
getline(fin,line[0]);
cout<<line[0]<<endl;
}
for(int contor=1; contor<5; contor++)
{
do
{
getline(fin,line[contor]);
}
while(line[contor].size()==0);
cout<<line[contor]<<'\n';
}
cout<<"Select your answer: ";
cin >> answer;
if(answer==easy[cnt++])total++;
line[0]="";
}
cout<<total<<endl;
}
Here is one was of doing it, making use of objects and vectors:
struct quiz_question
{
auto print_question() -> void
{
char prefix[] = { 'a', 'b', 'c', 'd' };
std::cout << "Question " << question_number << ": " << question << '\n';
for (auto x = 0; x < possible_answers.size(); x++)
{
std::cout << prefix[x] << ". " << possible_answers[x] << '\n';
}
}
auto check_answer(char user_answer) -> bool { return user_answer == answer; }
std::size_t question_number;
std::string question;
std::array< std::string, 4 > possible_answers;
char answer;
};
int main()
{
std::vector< quiz_question > questions;
std::string number_of_questions_str;
std::size_t number_of_questions;
std::ifstream ifs("questions.txt");
if (!ifs)
{
std::cout << "Cannot open questions.txt!\n";
exit(1);
}
// Start loading in questions.
// Read first line, describes how many questions there are.
std::getline(ifs, number_of_questions_str);
ifs.ignore(10000, '\n'); // Ignore a line
try
{
number_of_questions = std::stoul(number_of_questions_str);
}
catch (std::invalid_argument &ec)
{
std::cout << "Unable to parse questions.txt!\n";
exit(1);
}
// Load each question in.
for (auto x = 0; x < number_of_questions; x++)
{
quiz_question current_question;
current_question.question_number = x + 1;
// Read the question line
std::getline(ifs, current_question.question);
// Read the possible answers
for (auto &possible_answer : current_question.possible_answers)
{
std::getline(ifs, possible_answer);
}
// Read the actual answer
current_question.answer = ifs.get();
ifs.ignore(10000, '\n'); // Ignore the rest of that line
questions.push_back(std::move(current_question));
ifs.ignore(10000, '\n'); // Ignore a line
}
// Now all the questions have been loaded. Lets start the quiz!
char answer;
std::size_t score { 0 };
std::cout << "Starting the quiz!\n";
for (auto &question : questions)
{
question.print_question(); // Print question and possible answers
std::cin >> answer;
if (question.check_answer(answer))
{
std::cout << "Correct!\n\n";
score++;
}
else
{
std::cout << "Incorrect!\n\n";
}
std::cin.clear(); // Clear flags
std::cin.ignore(10000, '\n'); // Skip to next line
}
}
I've had to change your questions.txt format a little also, its described below:
questions.txt
2
WHEN COMPUTER WAS FIRST INVENTIONED?
1822
1823
1834
1922
a
WHO KILLED PRESEDENT BENOGIR VUTTO?
nawaz shrif
pervase
non of them
political leder
c
The first line is your total number of questions.
Blank line.
Question line
Answer A
Answer B
Answer C
Answer D
Correct answer
Blank line
Repeat numbers 3-9
Hope this helps
I am trying to allow only integrers to be inputted
Should Reject:
5h
3.4
3.gh
3.0
htr
Should Accept:
-5
0
78
Current Code
int getIntInput() {
int userInput;
while (true) {
std::cout << "> ";
std::cin >> userInput;
std::cout << std::flush;
if (std::cin.fail()) {
std::string cinBuffer;
std::cin.clear();
std::getline(std::cin, cinBuffer);
continue;
}
break;
}
return userInput;
}
Updated Code
Issues:
Accepts all rejections excluding "htr" (No numerals)
int getIntInput() {
std::string rawInput;
int parsedinput;
while (true) {
std::cout << "> ";
std::getline(std::cin, rawInput);
std::cout << std::flush;
try {
parsedinput = std::stoi(rawInput);
} catch (std::invalid_argument & e) {
continue;
} catch (std::out_of_range & e) {
continue;
}
break;
}
return parsedinput;
}
Finished Code
Accepts only integers with an optional parameter which will allow
negative numbers to be accepted or rejected.
int getIntInput(bool allowNegatives = true) {
bool validIntInput;
std::string rawInput;
int parsedinput;
while (true) {
validIntInput = true;
// Grabs the entire input line
std::cout << "> ";
std::getline(std::cin, rawInput);
std::cout << std::flush;
for (int i = 0; i < rawInput.length(); i++) {
// Checks to see if all digits are a number as well as to see if the number is a negative number
if (!isdigit(rawInput[i]) && !(allowNegatives && i == 0 && rawInput[i] == '-')) {
validIntInput = false;
break;
}
}
if (!validIntInput) {
continue;
} else {
try {
// Try parse the string to an int
parsedinput = std::stoi(rawInput);
// Catch all possible exceptions, another input will be required
} catch (...) {
continue;
}
// If the code reaches here then the string has been parsed to an int
break;
}
}
return parsedinput;}
The way cin works when having to read an int, is that it starts parsing the integer and stops when it finds a non-int character, storing the parsed int in the variable. That's why on float numbers it stops on the dot and in input '5g' it will stop on g.
What you can do instead if you only want integer input, is to read the whole line and then check if every character in your string is numeric, with the following piece of code:
bool onlyNums = true;
for (int i=0;i<rawInput.size();i++) {
if (!isdigit(rawInput[i]))
onlyNums = false;
}
if (!onlyNums)
continue;
(You have to include ctype.h library for the above code)
If you don't mind the overhead, I would grab the input from cin with cin.getline() and save it into a string. Then loop through the string and call isdigit on each char. You can discard the chars that aren't digits by useing the str.erase function.
You will need to #include cctype for isdigit().
Note: this will have at least O(N) runtime based on the length of your string.
template<class T>
T findInStreamLineWith(std::istream &input) {
std::string line;
while (std::getline(input, line)) {
std::istringstream stream(line);
T x;
if (stream >> x >> std::ws && stream.eof()) {
return x;
}
// reenter value
// cout << "Enter value again: ";
}
throw std::invalid_argument("can't find value in stream");
}
…
auto x = findInStreamLineWith<int>(std::cin);
Trying to tokenize a String in C++ which is read from a file, separated by commas, but I only need the first 3 data of every line.
For example:
The lines look like this:
140,152,2240,1,0,3:0:0:0:
156,72,2691,1,0,1:0:0:0:
356,72,3593,1,0,1:0:0:0:
But I only need the first 3 data of these lines. In this case:
140, 152, 2240156, 72, 2691356, 72, 3593
I'm trying to add these data into a vector I just don't know how to skip reading a line from the file after the first 3 data.
This is my current code: (canPrint is true by default)
ifstream ifs;
ifs.open("E:\\sample.txt");
if (!ifs)
cout << "Error reading file\n";
else
cout << "File loaded\n";
int numlines = 0;
int counter = 0;
string tmp;
while (getline(ifs, tmp))
{
//getline(ifs, tmp); // Saves the line in tmp.
if (canPrint)
{
//getline(ifs, tmp);
numlines++;
// cout << tmp << endl; // Prints our tmp.
vector<string> strings;
vector<customdata> datalist;
istringstream f(tmp);
string s;
while (getline(f, s, ',')) {
cout << s << " ";
strings.push_back(s);
}
cout << "\n";
}
How about checking the size of the vector first? Perhaps something like
while (strings.size() < 3 && getline(f, s, ',')) { ... }
Those are the parts of the code I have:
ifstream inFile;
inFile.open("Product1.wrl");
...
if (!inFile.is_open()){
cout << "Could not open file to read" << endl;
return 0;
}
else
while(!inFile.eof()){
getline(inFile, line);
cout << line << endl; //this statement only to chech the info stored in "line" string
if (line.find("PointSet"))
inFile >> Point1;
}
The output shows me the same string over and over again. So this means that the cursor inside the file does not proceed and getline reads the same line.
What might be the problem of this odd behavior?
If this is relevant:
The file does open as a .txt file and contains the exact information I need.
Okay I figured the problem:
Even after first eteration the return value of line.find("PointSet")is: 429467295... while my line string contains only one letter "S". Why?
Change
while(!inFile.eof()){
getline(inFile, line);
to
while( getline(inFile, line) ) {
I don't know why people get bitten by eof() quite so often, but they do.
Mixing getline with >> is problematic, because the >> will leave a '\n' in the stream, so the next getline will come back empty. Change that to use getline as well.
if (line.find("PointSet")) isn't what you want either. find returns the position in the string, or std::string::npos if it wasn't found.
Also, you can change
ifstream inFile;
inFile.open("Product1.wrl");
to
ifstream inFile ("Product1.wrl");
Here's a version showing the reads:
class Point
{
public:
int i, j;
};
template <typename CharT>
std::basic_istream<CharT>& operator>>
(std::basic_istream<CharT>& is, Point& p)
{
is >> p.i >> p.j;
return is;
}
int main()
{
Point point1;
std::string line;
while(std::getline(std::cin, line))
{
std::cout << line << '\n'; //this statement only to chech the info stored in "line" string
if (line.find("PointSet") != std::string::npos)
{
std::string pointString;
if (std::getline(std::cin, pointString))
{
std::istringstream iss(pointString);
iss >> point1;
std::cout << "Got point " << point1.i << ", " << point1.j << '\n';
}
else
{
std::cout << "Uhoh, forget to provide a line with a PointSet!\n";
}
}
}
}