Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed last month.
Improve this question
I'm fairly new to C++. I'm creating a program that uses multiple .dat input files. In these input files <double>, <int>, <char> and <bool> variable definitions are stored. Variables are declared within the program itself. I want the input files to be somewhat user friendly, therefore I added text to clarify.
I'm using ifstream with overload parameter >> to read the input files. To ignore redundant text I'm using .ignore(256, '\n') and to find a variable definition .ignore(256, '=').
The code I'm using works, but it looks ridiculous. How can I improve this code to just read all the lines with a = symbol?
Below a representation of what I'm doing right now. I chose .dat for input because I'm using .txt for output. It just makes the files a bit more recognizable, but I don't mind changing to .txt if necessary.
inputfile.dat
IGNORE THIS LINE
IGNORE THIS LINE
IGNORE THIS LINE
IGNORE THIS LINE
IGNORE THIS LINE
var1 = 1 2 3
var2 = 4.5 5.5 6.5
var3 = 7 8.5 9
...
varX = 10.0 11 12.0
IGNORE THIS LINE
IGNORE THIS LINE
IGNORE THIS LINE
var4 = 99
var5 = B
var6 = 0
IGNORE THIS LINE
IGNORE THIS LINE
IGNORE THIS LINE
v = 0
longvarname = 100
var whitespace = 0
main.cpp
int main (){
std::ifstream myfile1 ("inputfile.dat");
if (myfile1.is_open()){
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '=');
myfile1 >> var1_1 >> var1_2 >> var1_3;
myfile1.ignore(256, '=');
myfile1 >> var2_1 >> var2_2 >> var2_3;
myfile1.ignore(256, '=');
myfile1 >> var3_1 >> var3_2 >> var3_3;
myfile1.ignore(256, '=');
...
myfile1.ignore(256, '=');
myfile1 >> varX_1 >> varX_2 >> varX_3;
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '=');
myfile1 >> var4;
myfile1.ignore(256, '=');
myfile1 >> var5;
myfile1.ignore(256, '=');
myfile1 >> var6;
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '\n');
myfile1.ignore(256, '=');
myfile1 >> v;
myfile1.ignore(256, '=');
myfile1 >> longvarname;
myfile1.ignore(256, '=');
myfile1 >> var_whitespace;
}
else{
std::cerr << "ERROR! No access to inputfile.dat. Press ENTER to exit." << std::endl;
std::cin.get();
exit(1);
}
return 0
}
I've tried looking into getline() and splitting the line but I don't fully understand that. I don't want "strings" because all my variables are anything but a string (apart from one <char>).
I would scan this file line by line into a string.
Then try to see if that line contains a = in the line.
std::string line;
while (std::getline(myfile1), line)
{
if (line.find('=') != std::string::npos) {
handleImportant(line);
}
}
Now what you do with the line depends on your file definition.
It is not clear what your file definition is from the question.
Some lines have numbers, some have a set of numbers and one seems to have a character. It is unclear what is expected of the shown variables are the only variables.
But if we assume the simplist format:
<Variable Name> = <Some Text>
Then for each variable. We can store the associated text. We can split the text using a string stream.
std::map<std::string, std::string> variableMap;
void handleImportant(std::string const& line)
{
std::stringstream lineStream(line);
std::string variable;
char assign = 'X';
if (lineStream >> variable >> assign && assign == '=') {
// The rest of the line
// Or you can try and break up the line and convert
// to a specific type.
std::string restOfLine;
std::getline(lineStream, restOfLine);
variableMap[variable] = restOfLine;
}
}
You want something like
std::string line;
std::getline(myfile1, line); // note: give myfile1 a more descriptive name
// easier-to-read code is easier-to-debug as well
std::istringstream tokenizer(line);
std::string key;
std::string value;
if (std::getline(tokenizer, key, '=') && std::getline(tokenizer, value)
{
// use key and value
// key and value will be raw string data, whitespace and all, and
// will certainly require some amount of massaging into a more usable
// form. Remy raises the point of trimming the whitespace in the comments
// and in this case the string of nymbers will likely need to be
// transformed into a vector of an actual numeric type.
}
Wrap this up into a function and call the function in a loop to consume the file.
Related
just to understand how to read correctly, how could i read the next text from file, if i want to read the diferent strings in each line. Each line can have different sizes (1st line could have 3 strings and 2nd line could have 100 strings)
2 //Number of lines
A AS BPST TRRER
B AS BP
I tried in my code something like this, but i dont know how to check if program it's in the end of line.
ifstream fich("thefile.txt");
fich >> aux; //Contain number of line
for(int i=0;i<aux;i++){ //For each line
string line;
getline(fich, line);
char nt; //First in line it's always a char
fich >> nt;
string aux;
while(line != "\n"){ //This is wrong, what expression should i use to check?
fich >> aux;
//In each read i'll save the string in set
}
}
So at the end, i want that set contains: {{A,AS,BPST,TRRER} {B,AS,BP}}
Thanks.
while(line != "\n"){ //This is wrong, what expression should i use to check?
Yes, because the '\n' was removed by the getline() function.
Using std::istringstream it is easy to parse an arbitrary number of words up to the end of the current line:
string aux;
std::istringstream iss(line);
while(iss >> aux) {
// ...
}
Also note:
fich >> aux; //Contain number of line
will leave you with an empty line read with std::getline() because in this case the '\n' will be leftover from that operation (see Using getline(cin, s) after cin for more detailed information).
I need to read from file a series of information that is separated by commas
example
Orionis, 33000, 30000, 18, 5.9
Spica, 22000, 8300, 10.5, 5.1
i'm having a hard time figuring out the getline structure to make this work. The CS tutor, in the lab, says to use a getline for this but i can't seem to make it work (visual studio doesn't recognize getline in this function)
#include <iostream>
#include <fstream>
#include "star.h"
#include <string>
using namespace std;
char getChoice();
void processSelection(char choice);
void processA();
(skipping crap you don't need)
static char filePath[ENTRY_SZ];
void processA() {
ifstream openFile;
long temp, test;
double lum, mass, rad;
char name;
cout << "Please enter the full file path" << endl;
cin >> filePath;
openFile.open(filePath, ios::in);
if (openFile.good() != true) {
cout << "this file path was invalid";
}
while (openFile.good())
{
star *n = new star;
// getline(openFile, name, ',');
star(name);
getline(openFile, temp, ',');
n->setTemperature(temp);
getline(openFile, lum, ',');
n->setLuminosity(lum);
getline(openFile, mass, ',');
n->setMass(mass);
cin >> rad;
n->setRadius(rad);
}
}
From what i'm reading online (including older posts) and what my CS tutor says this should work so any help will be appreciated.
The suggestion to use std::getline() is likely implying that you'd first read a std::string and then deal with the content of this std::string, e.g., using std::istringstream.
I'd suggest not to use std::getline() and, of course, to also check inputs after they are read. To deal with the comma separator after non-std::string fields I'd use a custom manipulator:
std::istream& comma(std::istream& in) {
if ((in >> std::ws).peek() == ',') {
in.ignore();
}
else {
in.setstate(std::ios_base::failbit);
}
return in;
}
This manipulator skips leading whitespace (using the manipulator std::ws) and then simply checks if the next character is a comma. If so, the comma is extracted, otherwise the stream is set into failure mode and further attempts to read will fail until the failure state is dealt with (e.g., by using in.clear() and probably getting rid of any offending characters).
With this manipulator it is easy to read the respective values. Note, that when switching from formatted to unformatted input it is likely necessary that leading whitespace (e.g., in this case line breaks) need to be ignored. Also, the code below first attempts to read the values and uses them only when this attempt was successful: input shall always be checked after a read attempt was made, not before!
// ...
long temp;
double lum, mass, rad;
std::string name;
while (std::getline(in >> std::ws, name, ',')
>> temp >> comma
>> lum >> comma
>> mass >> comma
>> rad) {
// use the thus read values
}
I'm doing an exercise for the college and I have to compare a string added including the header <string>, and a character.
I have a text file with a few lines of data from a census, like
Alabama AL 4849377 Alaska AK 736732 Arizona AZ 6731484
I want to read the state name of each line with a string variable, but the comparison is the only thing that I am asking for, because is where I have the error.
I have this fragment of code:
struct Census{
string name;
int population, code;
};
struct States{
Census state;
};
typedef States Vector[US_STATES];
void loadCensus(ifstream & census, Vector stats){
int i=0;
string readData;
string line;
while (getline(census, line)) {
stringstream linestream(line);
while (linestream >> readData) {
if (linestream >> stats[i].state.name >>
stats[i].state.code >>
stats[i].state.population)
{
std::cerr << "Bad input on line " << i << ": " << line << std::endl;
}
stats[i].state.name=readData;
stats[i].state.code=readData;
stats[i].state.population=readData;
i++;
}
}
}
How I should convert readData to an integer to assign stats[i].state.population=readData?
I get an error in line 17 in the linestream >> readData.
You want to use the getline() function instead.
I think ita a member function of ifstream or either compare the not readData to a string ("\n") - double quotation. Or put the read data into a string and check if the sting contains a '\n'.
census >> readData will read the next word (any group of non-whitespace characters) from the input. In order to do this, it will discard all whitespace on its hunt for the next word. '\n' is whitespace, so you will never read it with the >> operator without playing games you probably don't want to play.
Instead of >>, use std::getline to read a line and then use a std::stringstream to break the line up into words.
std::string line;
while (std::getline(census, line)) {
std::stringgstream linestream(line);
while (linestream >> readData) {
statistics.state[i]=readData;
i++;
}
}
But...
I do not believe statistics.state[i]=readData; does quite what you want to do. You probably want something more like:
std::string line;
while (std::getline(census, line)) {
std::stringstream linestream(line);
if (!(linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population))
{
std::cerr << "Bad input on line " << i << ": " << line << std::endl;
}
i++;
}
In this state becomes an array or vector of objects that probably looks something like
struct statestats
{
std::string name;
std::string abbreviation;
int population;
};
Breaking it down line by line
std::stringstream linestream(line);
Makes a stringstream. A string stream is a stream like cin and cout or a fstream, but it contains a string. The main use is to buffer and build strings with the same syntax you would use on another stream. In this case we are use it to split up the line into words.
if (linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population)
Needs to be handled in a few parts in a few parts. Over all it is an abbreviation of
if (linestream >> statistics.state[i].name &&
linestream >> statistics.state[i].abbreviation &&
linestream >> statistics.state[i].population)
Each stage of which reads from the linestream into a variable.
Next, the >> operator returns the stream being read, and this is used two ways in the example. The first allows chaining. The output of one >> is used as the input of the next, so if you look at >> as you would a function (and it is a function. See Stream extraction and insertion for more) you can think about it looking something like this:
linestream.read(statistics.state[i].name).read(statistics.state[i].abbreviation).read(statistics.state[i].population)
The >> syntax just makes it easier.
The next advantage you get from returning the stream is the stream can be tested to see if the stream is still good. It has a boolean operator that will return true if the stream is in a good state and can be used.
if(linestream)
{
good
}
else
{
bad
}
will enter good if the stream is open, has not reached the end of the stream, and has had no troubles reading or writing data.
Going back to our example
if (linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population)
Will enter the body of the if statement if the stream successfully read all three values from the stream. Which is not what we want. Ooops. I've corrected the above code already.
if (!(linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population))
will enter the body of the if if at least one value was not read for any reason and print out an error message. Normally when there is an error you will need to clear the error before continuing, but in this case we've use the whole stream and are about to discard it.
Assuming no error occurred all of the data from this line has been read and there is no need to
stats[i].state.name=readData;
stats[i].state.code=readData;
stats[i].state.population=readData;
Alright, this is for a project that's supposed to read in some code from a file with information on a student. However, it doesn't seem to be reading in anything from the file. I'm working in Visual Studio, and when I run it and pause at the end, I look at the vector I've created and it says there are no students in it. Student is a class I've created.
ifstream input;
string student_file;
cin >> student_file;
input.open(student_file);
double id_number = 0;
while (input >> id_number)
{
string name = "";
string address = "";
string phone_number = "";
cin.sync();
getline(input, name);
cin.sync();
getline(input, address);
cin.sync();
getline(input, phone_number);
Student * s = new Student(id_number, name, address, phone_number);
students.push_back(s);
}
The student class should be set up properly with a constructor and everything, so I'm assuming it's this code that's giving me the trouble. Even some direction on whether the problem is with the loop or my use of getlines would be helpful. Thanks!
This line:
while (input >> id_number)
extracts the integer from the input stream into id_number. When the extraction is finished, the newline character is still left in the stream. std::getline() is programmed to terminate extraction upon the discovery of the newline character (among other specifications).
To circumvent this situation, you need to discard the newline by using the std::ws manipulator. std::ws discards only leading whitespace from the stream. Newlines are considered whitespace. It is also advised that you check that your input succeeded by encasing the extraction in an if statement:
if (std::getline(input >> std::ws, name) &&
std::getline(input >> std::ws, address) &&
std::getline(input >> std::ws, phone_number))
Notice that I've also removed the std::cin.sync() between calls. Syncing the input buffer is not necessary in this context as stream insertion is not being performed, thus changes to the external sequence don't need to be regarded. Besides, the idea of syncing the input buffer of std::cin is non-nonsensical as std::cin is not affected whatsoever by the actions being taken on input.
And lastly, your vector that holds the student pointers should instead hold the actual objects themselves. This avoids the need for using dynamic memory allocation. The type of the vector should therefore be std::vector<Student>. And this is how you would construct the element in the vector:
students.emplace_back(id_number, name, address, phone_number);
Here is the full code:
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::ifstream input;
std::string student_file;
std::cin >> student_file;
input.open(student_file);
double id_number = 0;
std::vector<Student> students;
while (input >> id_number)
{
string name;
string address;
string phone_number;
if (std::getline(input >> std::ws, name) &&
std::getline(input >> std::ws, address) &&
std::getline(input >> std::ws, phone_number))
{
students.emplace_back(id_number, name, address, phone_number);
}
}
}
I am trying to read through a file and get specific strings on each line. The end of the string that i need is marked by a semicolon. I have no problems doing this, but I am noticing that getline() with the delimiter is automatically attaching a new line to my string.
filename.open(FileName);
while(filename)
{
getline(filename, name[counter], ';');
filename >> amount[counter] >> unit[counter] >> calories[counter];
counter++;
}
So when i would go to print out the name array there would be 1 extra line break that I had not put there myself as if there was an extra '\n' being picked up along the way. Does anyone have a solution? An example of the file format that I am reading from is below.
Dave Jones; 24 Tall
Jillian Jones; 34 Short
etc...
After running
filename >> amount[counter] >> unit[counter] >> calories[counter];
the newline is still in the buffer. This normally isn't a problem when you are using only ">>"; it just ignores newlines. But when you mix getline and ">>" you need to ignore the newlines that ">>" leaves behind. Try something like this:
filename >> amount[counter] >> unit[counter] >> calories[counter];
// Ignore first character or everything up to the next newline,
// whichever comes first
filename.ignore(1, '\n');
This is a bit redundant, but it's easy to read.
Better way would be to read file line by line into a buffer and then split strings by ';':
while(true) {
std::string line;
std::getline( in, line );
if( !in ) break;
std::istringstream iline( line );
while(true) {
std::string str;
std::getline( iline, str, ';' );
if( !iline ) break;
// you get string by string in str here
}
}
A simpler way to swallow the whitespace:
filename >> amount[counter] >> unit[counter] >> calories[counter] >> std::ws;