Yesterday I made a small script with some help to read .csv files. I though I found a way to read the first value and store it, but for some reason it stores the last value instead.
I store what I thought should be the first value under value1, and re-display it to make sure it displays properly and is in fact being stored under a callable variable.
Does anyone know what is wrong with this code? I think I should be using vectors but as I read the reference sheets I find on the internet about them I am being a little thrown of. Any help is appreciated.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main ()
{
int loop = 1;
string value;
string value1;
while(loop = 1)
{
cout << "Welcome! \n" << endl;
ifstream myfile;
myfile.open ("C:/Documents and Settings/RHatfield/My Documents/C++/Product Catalog Creator/Source/External/Sample.csv");
while (myfile.good())
getline ( myfile, value, ',' );
cout << string ( value) << endl;
value1 = value;
cout << value1;
myfile.close();
system("PAUSE");
return 0;
}
}
Your error seems to be in pure code formatting.
while (myfile.good())
There is no {} after the loop. So only next line is repeated.
The following code is executed after reading of the whole file.
cout << string ( value) << endl;
Thus value stores the last line of the file.
You may want to change the condition in your while loop:
char separator;
int value1;
int value2;
int value3;
while (myfile >> value1)
{
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> value2;
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> value3;
// Ignore the newline, as it is still in the buffer.
myfile.ignore(10000, '\n');
// Process or store values.
}
The above code fragment is not robust but demonstrates the concept of reading from a file, skipping non-numeric separators and processing the end of the line. The code is optimized either.
Related
I have been working on this for a while and can't fix it. I am very new to C++. So far I can get 10 things into my array but the output is not legible, it's just a bunch of numbers. I have read other posts with similar code but for some reason mine isn't working.
The input text file is 10 lines of fake data like this:
56790 "Comedy" 2012 "Simpsons" 18.99 1
56791 "Horror" 2003 "The Ring" 11.99 7
My code is here:
(My output is below my code)
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
struct DVD {
int barcode;
string type;
int releaseDate;
string name;
float purchaseprice;
int rentaltime;
void printsize();
};
int main () {
ifstream in("textfile.exe");
DVD c[10];
int i;
for (int i=0; i < 10; i++){
in >> c[i].barcode >> c[i].type >> c[i].releaseDate >>
c[i].name >> c[i].purchaseprice >> c[i].rentaltime;
}
for (int i=0;i< 10;i++) {
cout << c[i].barcode<<" ";
cout << c[i].type<<" ";
cout << c[i].releaseDate<<" ";
cout << c[i].name << " ";
cout << c[i].purchaseprice << " ";
cout << c[i].rentaltime << "\n";
}
return 0;
}
My output looks similar to garbage, but there are 10 lines of it like my array:
-876919876 -2144609536 -2.45e7 2046
A comment on what to study to modify my code would be appreciated.
As suggested by cmbasnett, ifstream in("textfile.exe") reads in an executable file. If you with for the program to read in a text file, changing it to ifstream in("textfile.txt") should work.
You always need to check that your input is actually correct. Since it may fail prior to reading 10 lines, you should probably also keep a count of how many entries you could successfully read:
int i(0);
for (; i < 10
&& in >> c[i].barcode >> c[i].type >> c[i].releaseDate
>> c[i].name >> c[i].purchaseprice >> c[i].rentaltime; ++i) {
// ???
}
You actual problem reading the second line is that your strings are quoted but the approach used for formatted reading of strings doesn't care about quotes. Instead, strings are terminated by a space character: the formatted input for strings will skip leading whitespace and then read as many characters until another whitespace is found. On your second line, it will read "The and then stop. The attempt to read the purchaseprice will fail because Ring isn't a value numeric value.
To deal with that problem you might want to make the name quotedstring and define an input and output operators for it, e.g.:
struct quoted_string { std::string value; };
std::istream& operator>> (std::istream& in, quoted_string& string) {
std::istream::sentry cerberos(in); // skips leading whitespace, etc.
if (in && in.peek() == '"') {
std::getline(in.ignore(), string.value, '"');
}
else {
in.setstate(std::ios_base::failbit);
}
return in;
}
std::ostream& operator<< (std::ostream& out, quoted_string const& string) {
return out << '"' << string.value << '"';
}
(note that the code isn't test but I'm relatively confident that it might work).
Just to briefly explain how the input operator works:
The sentry is used to prepare the input operation:
It flushes the tie()d std::ostream (if any; normally there is none except for std::cin).
It skips leading whitespace (if any).
It checks if the stream is still not in failure mode (i.e., neither std::ios_base::failbit nor `std::ios_base::badbit are set).
To see if the input starts with a quote, in.peek() is used: this function returns an int indicating either that the operation failed (i.e., it returns std::char_traits<char>::eof()) or the next character in the stream. The code just checks if it returns " as it is a failure if the stream returns an error or any other character is present.
If there is a quote, the quote is skipped using file.ignore() which by default just ignores one character (it can ignore more characters and have a character specified when to stop).
After skipping the leading quote, std::getline() is used to read from file into string.value until another quote is found. The last parameter is defaulted to '\n' but for reading quoted string using a '"' is the correct value to use. The terminating character is, conveniently, not stored.
I would like to extract and analyze data from a large text file. The data contains floats, integers and words.
The way I thought of doing this is to extract a complete line (up to newline) using std::getline(). Then extract individual data from the line extracted before (extract until whitespace, then repeat).
So far I have this:
int main( )
{
std::ifstream myfile;
myfile.open( "example.txt", std::ios::in );
if( !(myfile.is_open()) )
{ std::cout << "Error Opening File";
std::exit(0); }
std::string firstline;
while( myfile.good() )
{
std::getline( myfile, firstline);
std::cout<< "\n" << firstline <<"\n";
}
myfile.close();
return 0;
}
I have several problems:
1) How do I extract up to a whitespace?
2) What would be the best method of storing the data? There are about 7-9 data types, and the data file is large.
EDIT: An example of the file would be:
Result Time Current Path Requirements
PASS 04:31:05 14.3 Super_Duper_capacitor_413 -39.23
FAIL 04:31:45 13.2 Super_Duper_capacitor_413 -45.23
...
Ultimately I would like to analyze the data, but so far I'm more concerned about proper input/reading.
You can use std::stringstream to parse the data and let it worry about skipping the whitspaces. Since each element in the input line appears to require additional processing just parse them into local variables and after all post processing is done store the final results into a data structure.
#include <sstream>
#include <iomanip>
std::stringstream templine(firstline);
std::string passfail;
float floatvalue1;
std::string timestr;
std::string namestr;
float floatvalue2;
// split to two lines for readability
templine >> std::skipws; // no need to worry about whitespaces
templine >> passfail >> timestr >> floatvalue1 >> namestr >> floatvalue2;
If you do not need or want to validate that the data is in the correct format you can parse the lines directly into a data structure.
struct LineData
{
std::string passfail;
float floatvalue1;
int hour;
int minute;
int seconds;
std::string namestr;
float floatvalue2;
};
LineData a;
char sep;
// parse the pass/fail
templine >> a.passfail;
// parse time value
templine >> a.hour >> sep >> a.minute >> sep >> a.seconds;
// parse the rest of the data
templine >> a.timestr >> a.floatvalue1 >> a.namestr >> a.floatvalue2;
For the first question, you can do this:
while( myfile.good() )
{
std::getline( myfile, firstline);
std::cout<< "\n" << firstline <<"\n";
std::stringstream ss(firstline);
std::string word;
while (std::getline(ss,word,' '))
{
std::cout << "Word: " << word << std::endl;
}
}
As for the second question, can you give us more precision about the data types and what is it you want to do with the data once stored?
I have a file that has a number in which is the number of names that follow. For example:
4
bob
jim
bar
ted
im trying to write a program to read these names.
void process_file(ifstream& in, ofstream& out)
{
string i,o;
int tmp1,sp;
char tmp2;
prompt_user(i,o);
in.open (i.c_str());
if (in.fail())
{
cout << "Error opening " << i << endl;
exit(1);
}
out.open(o.c_str());
in >> tmp1;
sp=tmp1;
do
{
in.get(tmp2);
} while (tmp2 != '\n');
in.close();
out.close();
cout<< sp;
}
So far I am able to read the first line and assign int to sp
I need sp to be a counter for how many names. How do I get this to read the names.
The only problem I have left is how to get the names while ignoring the first number.
Until then i cannot implement my loop.
while (in >> tmp1)
sp=tmp1;
This successfuly reads the first int from the and then tries to continue. Since the second line is not an int, extraction fails, so it stops looping. So far so good.
However, the stream is now in fail state, and all subsequent extractions will fail unless you clear the error flags.
Say in.clear() right after the first while loop.
I don't really see why you wrote a loop to extract a single integer, though. You could just write
if (!(in >> sp)) { /* error, no int */ }
To read the names, read in strings. A loop is fine this time:
std::vector<std::string> names;
std::string temp;
while (in >> temp) names.push_back(temp);
You'd might want to add a counter somewhere to make sure that the number of names matches the number you've read from the file.
int lines;
string line;
inputfile.open("names.txt");
lines << inputfile;
for(i=0; i< lines; ++i){
if (std::getline(inputfile, line) != 0){
cout << line << std::endl;
}
}
First of all, assuming that the first loop:
while (in >> tmp1)
sp=tmp1;
Is meant to read the number in the beginning, this code should do:
in >> tmp1;
According to manual operator>>:
The istream object (*this).
The extracted value or sequence is not returned, but directly stored
in the variable passed as argument.
So don't use it in condition, rather use:
in >> tmp1;
if( tmp1 < 1){
exit(5);
}
Second, NEVER rely on assumption that the file is correctly formatted:
do {
in.get(tmp2);
cout << tmp2 << endl;
} while ( (tmp2 != '\n') && !in.eof());
Although whole algorithm seems a bit clumsy to me, this should prevent infinite loop.
Here's a simple example of how to read a specified number of words from a text file in the way you want.
#include <string>
#include <iostream>
#include <fstream>
void process_file() {
// Get file name.
std::string fileName;
std::cin >> fileName;
// Open file for read access.
std::ifstream input(fileName);
// Check if file exists.
if (!input) {
return EXIT_FAILURE;
}
// Get number of names.
int count = 0;
input >> count;
// Get names and print to cout.
std::string token;
for (int i = 0; i < count; ++i) {
input >> token;
std::cout << token;
}
}
I have the following .txt file:
test.txt
1,2,5,6
Passing into a small C++ program I made through command line as follows:
./test test.txt
Source is as follows:
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char **argv)
{
int temp =0;
ifstream file;
file.open(argv[1]);
while(!file.eof())
{
temp=file.get();
file.ignore(1,',');
cout<<temp<<' ';
}
return 0;
}
For some reason my output is not 1 2 5 6 but 49 50 53 54. What gives?
UPDATE:
Also, I noticed there is another implementation of get(). If I define char temp then I can do file.get(temp) and that will also save me converting ASCII representation. However I like using while (file >> temp) so I will be going with that. Thanks.
temp is an int. So you see the encoded ascii values after casting the char to an int.
49 is the ascii code for digit 49-48 = 1.
get() gives you a character (character code).
by the way, eof() only becomes true after a failed read attempt, so the code you show,
while(!file.eof())
{
temp=file.get();
file.ignore(1,',');
cout<<temp<<' ';
}
will possibly display one extraneous character at the end.
the conventional loop is
while( file >> temp )
{
cout << temp << ' ';
}
where the expression file >> temp reads in one number and produces a reference to file, and where that file objected is converted to bool as if you had written
while( !(file >> temp).fail() )
This does not do what you think it does:
while(!file.eof())
This is covered in Why is iostream::eof inside a loop condition considered wrong?, so I won't cover it in this answer.
Try:
char c;
while (file >> c)
{
// [...]
}
...instead. Reading in a char rather than an int will also save you having to convert the ascii representation (ASCII value 49 is 1, etc...).
For the record, and despite this being the nth duplicate, here's how this code might look in idiomatic C++:
for (std::string line; std::getline(file, line); )
{
std::istringstream iss(line);
std::cout << "We read:";
for (std::string n; std::getline(iss, line, ','); )
{
std::cout << " " << n;
// now use e.g. std::stoi(n)
}
std::cout << "\n";
}
If you don't care about lines or just have one line, you can skip the outer loop.
I was wondering If I could jump positions in a text file.
Suppose I have this file.
12
8764
2147483648
2
-1
Whenever I try to read the third number it won't read because its larger than the max number for a 32 bit int.So whenever i reach the third number, it keeps reading the second over and over again. How can I jump to the 4th number?
Use std::getline instead of operator>>(std::istream, int)
std::istream infile(stuff);
std::string line;
while(std::getline(infile, line)) {
int result;
result = atoi(line.c_str());
if (result)
std::cout << result;
}
The reason you are experiencing the behavior that you are, is that when the std::istream tries (and fails) to read in an integer, it sets a "badbit" flag which means that something went wrong. As long as that badbit flag remains set, it won't do anything at all. So it's not actually re-reading in that line, it's doing NOTHING, and leaving the value that had been there alone. If you want to keep more in line with what you already had, it's probably like below. The above code is simpler and less error prone though.
std::istream infile(stuff);
int result;
infile >> result; //read first line
while (infile.eof() == false) { //until end of file
if (infile.good()) { //make sure we actually read something
std::cout << result;
} else
infile.clear(); //if not, reset the flag, which should hopefully
// skip the problem. NOTE: if the number is REALLY
// big, you may read in the second half of the
// number as the next line!
infile >> result; //read next line
}
You can first read the line, then convert the line to integer if you can. Here is an example for your file :
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
int main()
{
std::ifstream in("file");
std::string line;
while (std::getline(in, line)) {
int value;
std::istringstream iss(line);
if (!(iss >> value)) {
std::istringstream iss(line);
unsigned int uvalue;
if (iss >> uvalue)
std::cout << uvalue << std::endl;
else
std::cout << "Unable to get an integer from this" << std::endl;
}
else
std::cout << value << std::endl;
}
}
As an alternative to using std::getline(), you could call std::ios::clear(). Consider this excerpt from your previous question
fin >> InputNum;
You could replace that code with this:
fin >> InputNum;
if(!fin)
fin.clear();