getline and file handling - c++

I want to read the first lines of 2 separate files and then compare them...the following is the code i use but it gives me "istream to string error". do i need to use a while condition to start reading the files first?
ifstream data_real(filename.c_str()); /*input streams to check if the flight info
are the same*/
ifstream data_test("output_check.txt");
string read1, read2;
string first_line_input = getline(is,read1);
string first_line_output_test = getline(data_test,read2);
string test_string1, test_string2;
int num_lines_output_test, num_lines_input;
if((first_line_input.substr(0,3)==first_line_output_test.substr(0,3)))
{
while(!data_test.eof()) // count the number of lines for the output test file with the first flight info
{
getline(data_test,test_string1);
num_lines_output_test++;
}
while(getline(is,test_string2)) // count the number of lines for the output test file with the first flight info
{
if(test_string2.substr(0,3)!="ACM")
num_lines_input++;
else
break;
}
}

getline(istream, string) returns a reference to the istream, not a string.
So, comparing the first line of each file could be something like:
string read1, read2;
if !(getline(is,read1) && getline(data_test,read2)){
// Reading failed
// TODO: Handle and/or report error
}
else{
if(read1.substr(0,3) == read2.substr(0,3)){
//...
Also: Never use eof() as a termination condition for a stream reading loop. The idiomatic way to write it is:
while(getline(data_test,test_string1)) // count the number of lines for the output test file with the first flight info
{
num_lines_output_test++;
}

Try adding this helper function:
std::string next_line(std::istream& is) {
std::string result;
if (!std::getline(is, result)) {
throw std::ios::failure("Failed to read a required line");
}
return result;
}
Now you can use lines from the file the way you want (i.e. to initialize strings, rather than modify them):
string first_line_input = next_line(is);
string first_line_output_test = next_line(data_test);

Related

Checking whether file contains only whitespace C++

I am trying to read input from a file in C++, and I need to give an error message if there is no input.
This statement works if the file is completely empty:
if (f.peek() == std::ifstream::traits_type::eof()) return error("Empty file");
where error() is a simple function:
int error(string message){
cerr << "ERROR: " << message << "\n";
return -1;
}
How can I check for files that contain only whitespace so I can raise the same error? Such as 7 newlines?
My should continue executing normally if the file contains anything but only whitespace characters.
Scanning the file in advance if it only contains whitespaces would be very inefficient.
I believe what you really need, is to keep track if some non-whitespace data could be read at all, and raise the error if not:
std::string line;
std::vector<std::string> lines_with_data;
while(std::getline(f,line)) {
// check if line is empty or contains only whitespace
if(!(line.empty() ||
(std::find_if_not(line.begin(),line.end(),std::isspace) != line.end())) {
lines_with_data.push_back(line);
}
}
if(lines_with_data.empty()) { // No data could be found
return error("Empty file");
}
reading characters and checking with std::isspace may help. something like this:
char c;
bool isEmpty = true;
while (f.get(c))
{
if(!std::isspace(c))
{
isEmpty = false;
break;
}
}
this way, isEmpty will be what it says
You could use an std::istream_iterator over the file input and check individual characters with std:isspace(), I suppose.
But I wonder if this is really what you want to do. Why not try to read whatever it is you want to read, and only worry about things when you fail your reading/parsing?

How can I convert a char array to a string in C++?

I have a char array called firstFileStream[50], which is being written to from an infile using fstream.
I want to convert this char array into a string called firstFileAsString. If I write string firstFileAsString = firstFileStream; it only writes the first word within the array and stops at the first space, or empty character. If I write firstFileAsString(firstFileStream) I get the same output.
How do I write the whole char array, so all words within it, to a string?
Here is the code to read in and write:
string firstInputFile = "inputText1.txt";
char firstFileStream[50];
ifstream openFileStream;
openFileStream.open(firstInputFile);
if (strlen(firstFileStream) == 0) { // If the array is empty
cout << "First File Stream: " << endl;
while (openFileStream.good()) { // While we haven't reached the end of the file
openFileStream >> firstFileStream;
}
string firstFileAsString = firstFileStream;
}
My problem, as zdan pointed out, is I was only reading the first word of the file, so instead I've used istreambuf_iterator<char> to assign the content directly to the string rather than the character array first. This can then be broken down into a character array, rather than the other way around.
openFileStream >> firstFileStream;
reads only one word from the file.
A simple example of reading the whole file (at least up to the buffering capacity) looks like this:
openFileStream.read(firstFileStream, sizeof(firstFileStream) - 1);
// sizeof(firstFileStream) - 1 so we have space for the string terminator
int bytesread;
if (openFileStream.eof()) // read whole file
{
bytesread = openFileStream.gcount(); // read whatever gcount returns
}
else if (openFileStream) // no error. stopped reading before buffer overflow or end of file
{
bytesread = sizeof(firstFileStream) - 1; //read full buffer
}
else // file read error
{
// handle file error here. Maybe gcount, maybe return.
}
firstFileStream[bytesread] = '\0'; // null terminate string

how to discard from streams? .ignore() doesnt work for this purpose, any other methods?

I have a lack of understanding about streams. The idea is, to read a file to the ifstream and then working with it. Extract Data from the stream to a string, and discard the part which is now in a string from the stream. Is that possible? Or how to handle those problems?
The following method, is for inserting a file which is properly read by the ifstream. (its a text file, containing informations about "Lost" episodes, its an episodeguide. It works fine, for one element of the class episodes. Every time i instantiate a episode file, i want to check the stream of that file, discard the informations about one episode (its indicated by "****", then the next episode starts) and process the informations discarded in a string. If I create a new object of Episode I want to discard the next informations about the episodes after "****" to the next "****" and so on.
void Episode::read(ifstream& in) {
string contents((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
size_t episodeEndPos = contents.find("****");
if ( episodeEndPos == -1) {
in.ignore(numeric_limits<char>::max());
in.clear(), in.sync();
fullContent = contents;
}
else { // empty stream for next episode
in.ignore(episodeEndPos + 4);
fullContent = contents.substr(0, episodeEndPos);
}
// fill attributes
setNrHelper();
setTitelHelper();
setFlashbackHelper();
setDescriptionHelper();
}
I tried it with inFile >> words (to read the words, this is a way to get the words out of the stream) another way i was thinking about is, to use .ignore (to ignore an amount of characters in the stream). But that doesnt work as intended. Sorry for my bad english, hopefully its clear what i want to do.
If your goal is at each call of Read() to read the next episode and advance in the file, then the trick is to to use tellg() and seekg() to bookmark the position and update it:
void Episode::Read(ifstream& in) {
streampos pos = in.tellg(); // backup current position
string fullContent;
string contents((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
size_t episodeEndPos = contents.find("****");
if (episodeEndPos == -1) {
in.ignore(numeric_limits<char>::max());
in.clear(), in.sync();
fullContent = contents;
}
else { // empty stream for next episode
fullContent = contents.substr(0, episodeEndPos);
in.seekg(pos + streamoff(episodeEndPos + 4)); // position file at next episode
}
}
In this way, you can call several time your function, every call reading the next episode.
However, please note that your approach is not optimised. When you construct your contents string from a stream iterator, you load the full rest of the file in the memory, starting at the current position in the stream. So here you keep on reading and reading again big subparts of the file.
Edit: streamlined version adapted to your format
You just need to read the line, check if it's not a separator line and concatenate...
void Episode::Read(ifstream& in) {
string line;
string fullContent;
while (getline(in, line) && line !="****") {
fullContent += line + "\n";
}
cout << "DATENSATZ: " << fullContent << endl; // just to verify content
// fill attributes
//...
}
The code you got reads the entire stream in one go just to use some part of the read text to initialize an object. Imagining a gigantic file that is almost certainly a bad idea. The easier approach is to just read until the end marker is found. In an ideal world, the end marker is easily found. Based on comments it seems to be on a line of its own which would make it quite easy:
void Episode::read(std::istream& in) {
std::string text;
for (std::string line; in >> line && line != "****"; ) {
text += line + "\n";
}
fullContent = text;
}
If the separate isn't on a line of its own, you could use code like this instead:
void Episode::read(std::istream& in) {
std::string text;
for (std::istreambuf_iterator<char> it(in), end; it != end; ++it) {
text.push_back(*it);
if (*it == '*' && 4u <= text.size() && text.substr(text.size() - 4) == "****") {
break;
}
if (4u <= text.size() && text.substr(text.size() - 4u) == "****") {
text.resize(text.size() - 4u);
}
fullContent = text;
}
Both of these approaches would simple read the file from start to end and consume the characters to be extracted in the process, stopping as soon as reading of one record is done.

C++ Read in file with only numbers (doubles)

I'm trying to read in a file that should contain only numbers in it. I can successfully read in the entire file if it meets that criteria, but if it so happened to have a letter in it, I need to return false with an error statement.
The problem is I'm finding it hard for my program to error when it finds this character. It can find it no problem, but when it does, it decides to just skip over it.
My code to read in the file and attempt to read in only numbers:
bool compute::Read (ifstream& stream)
{
double value;
string line;
int lineNumber = 1;
if (stream)
{
while (getline(stream, line))
{
lineNumber++;
istringstream strStream(line);
while (strStream >> value)
{
cout << value << endl;
}
}
}
return true;
}
The input file which I use for this is
70.5 61.2 A8 10.2
2
Notice that there is a non-number character in my input file. It should fail and return false at that point.
Currently, all it does is once it hits the "A", it simply returns to the next line, continuing the getline while loop.
Any help with this would be much appreciated.
The stringstream does catch those errors, but you're doing nothing to stop the enclosing loop from continuing when an error is found. You need to tailor your main loop so that it stops when the stringstream finds an error, which you can't do if the stringstream is being reconstructed on each iteration. You should create a for() loop instead and construct the stringstream in the declaration part. And the condition to the loop should be "as long as the stringstream and stream do not catch an error". For example:
for (std::istringstream iss; iss && std::getline(stream, line);)
{
iss.clear();
iss.str(line);
while (iss >> value)
{
std::cout << value << '\n';
}
}
Futhermore, it doesn't look like you need to use std::getline() or std::istringstream if you just want to print each value. Just do:
while (stream >> value) {
std::cout << value << '\n';
}
The above will stop when it finds an invalid character for a double.
You need the code to stop streaming but return false if it hasn't yet reached the end of the "input".
One way, possibly not the most efficient but still one way, to do that is parse a word at a time.
If you read first into a std::string and if it works (so the string is not empty) create an istringstream from that string, or reuse an existing one, and try streaming that into a double value.
If that fails, you have an invalid character.
Of course you can read a line at a time from the file, then split that into words, so that you can output a meaningful error message showing what line the bad text was found.
The issue of reading straight into doubles is that the stream will fail when it reaches end of file.
However it is possible to workaround that too because the reason for failing has an error status which you can check, i.e. you can check if it eofbit is set. Although the f in eofbit stands for "file" it applies to any stream not just files.
Although this method may sound better than reading words into a string first, I prefer that method in normal circumstances because you want to be able to report the error so you'll want to print in the error what was read.

Basic C++ program, getline()/parsing a file

I've been tasked with creating a small program that is to parse through a text file and grab necessary info from it. The file is laid out as such
Tuesday*Info5051*10:00*11:00*M3039*Info5064*12:00*3:00*G1001;
Basically it's supposed to store each string in a struct so that I can later retrieve it, but I'm unable to get my program to work (I have a learning disability so things tend to get difficult). Here's my code so far. (I know it's a simple program but I tend to overthink/screw up stuff.) My big problem I've hit so far is that it won't open the file to start. I've saved the file to the bin->debug as well as the main folder of the program. I'm sure I'm using the getline method wrong.
struct Course
{
string _sDay;
string _sName;
string _sCode;
string _iStart;
string _iDuration;
string _sRoom;
};
int main()
{
ifstream fileIn;
fileIn.open("courseLoad.txt");
vector<Course> vCourse;
string str="*";
string line;
if (!fileIn)
{
cout<<"A error has occured, please contact support.";
}
while(!fileIn.eof())
{
for(int i=0; i!= fileIn.eof();i++)
{
//file.getline(entry.part_num, 6, '-');
getline(fileIn,line,'*');
vCourse[i]._sDay =line;
getline(fileIn,line,'*');
vCourse[i]._sName =line;
getline(fileIn,line,'*');
vCourse[i]._sCode = line;
getline(fileIn,line,'*');
vCourse[i]._iStart =line;
getline(fileIn,line,'*');
vCourse[i]._iDuration = line;
getline(fileIn,line,'*');
vCourse[i]._sRoom =line;
cout<<vCourse[i];
}//end for
}
--output to screen here--
There are several issue with this code:
1) That code is missing a return statement or an else statement to prevent the program from continuing its execution in case it cannot open the file:
if (!fileIn)
{
cout<<"A error has occured, please contact support.";
return -1;
}
2) Your getline all operate on the same input stream. You want to read in a line, then parse that line. For example:
// Read in a line
while (getline(fileIn,line))
{
string item;
std::stringstream sstr(line);
// Read in an item
while (getline(sstr, item, "*"))
{
std::cout << item << std::endl;
}
}
3) vCourse size is 0, so you cannot use the [] operator; but you can use push_back to expand the size of the vector and insert an element at the back of the vector:
// Read in a line
while (getline(fileIn,line))
{
string item;
// Default course construction
Course c;
std::stringstream sstr(line);
// Read in an item
getline(sstr,item,'*');
c._sDay = item;
getline(sstr,item,'*');
c._sName = item;
getline(sstr,item,'*');
c._sCode = item;
getline(sstr,item,'*');
c._iStart = item;
getline(sstr,item,'*');
c._iDuration = item;
getline(sstr,item,'*');
c._sRoom = item;
// Save the course into the vector
vCourse.push_back(c);
}
You could also add some more error checking in the above (in case some elements are missing from the line).
One immediate problem that is clear is that you are not actually adding any Course structs into your vector but you are assigning to the elements of them as if you are. For example
vCourse[i]._sDay =line;
but you have not actually added an instanct of a Course struct to the vector at index i. This means you assign to an instance that is not present and that is never good news. What you need prior to this is
Course newItem; // make a new Course object instance
vCourse.push_back(newItem); // This adds the instance to the end of the vector
// Now assign to the members of vCourse[i];
vCourse[i]._sDay =line;
getline(fileIn,line,'*');
vCourse[i]._sName =line;
getline(fileIn,line,'*');
vCourse[i]._sCode = line;
getline(fileIn,line,'*');
vCourse[i]._iStart =line;
getline(fileIn,line,'*');
vCourse[i]._iDuration = line;
getline(fileIn,line,'*');
then you can assign to the struct.
Also if you want to do this
cout<<vCourse[i];
you will need to overload the operator<<
If you are unable to open your file, you need to check that you have 1) spelled the filename correctly and 2) that the file is in the same location as your executable. Probably would be safer to write the full pathname anyway
You can also try to put the content of file into single string and use strtok() function.