I have a .txt file that looks like this:
Toy ball
1.25
DVD
20.00
Row Boat
1.00
The first line is the item, the second is the price, and it repeats for the next 2 items and so on. So my question is how can i take the first line and put it in a string, and the second line and put it in a float value? Here is the code i have, i am using structs.
const int TOTAL_ITEMS = 3;
for (int itemNumber = 0; itemNumber < TOTAL_ITEMS; itemNumber++) {
string line;
getline(inFile,line);
list[itemNumber].item = line;
float cost;
inFile >> cost;
list[itemNumber].price = cost;
}
I then print the list:
for (int itemNumber = 0; itemNumber < TOTAL_ITEMS; itemNumber++) {
cout << itemNumber << ": " << list[itemNumber].item << " " << list[itemNumber].price << endl;
Now this doesnt print what i want it to, the struct array only took in the first line and the first price and printed it out, but the others all said "0". Any help would be awesome. Thanks guys
The float input (by the way, just use double, less trouble) leaves a newline in the input buffer, which is consumed by the subsequent line input.
Instead, use getline for both lines.
Then use e.g. istringstream to convert the second line to ... double, just by "reading" from it in the same as you'd read from inFile.
In addition to that direct bug fix, you need to check the stream state of inFile, e.g. by calling its fail member function. When you use the stream object directly as a condition, its conversion to bool calls fail for for you, and negates the result so that the stream object itself denotes "not fail". Note that good is not the opposite of fail, and also note that a fail state is persistent and causes subsequent input operations to be ignored.
You need to check the stream state as you consume input. You can do that by checking the return value of getline() and operator>>:
if (!getline(inFile,line))
; // oops, error, do something
if (!(inFile >> cost))
; likewise
Once you do this you will find that your input is failing because you're reading things slightly incorrectly, but it shouldn't be hard to fix once you see where it's broken.
Related
I am trying to write a code in C++ reading a text file contains a series of numerics. For example, I have this .txt file which contains the following series of numbers mixed with a character:
1 2 3 a 5
I am trying to make the code capable of recognizing numerics and characters, such as the 4th entry above (which is a character), and then report error.
What I am doing is like
double value;
while(in) {
in >> value;
if(!isdigit(value)) {
cout << "Has non-numeric entry!" << endl;
break;
}
else
// some codes for storing the entry
}
However, the isdigit function doesn't work for text file. It seems when I am doing in >> value, the code will implicitly type-cast a into double.
Can anyone give me some suggestion?
Thanks a lot!
Your while loop doesn't do what you think it does.
It only iterates one statement:
in >> value;
The rest of the statements are actually outside the loop.
Using curly braces for the while body is always recommended
I created a small mini script where I would be reading in a file through a standard fstream library object as I was a little unsure on what your "in" represented.
Essentially, try to read in every element as a character and check the digit function. If you're reading in elements that are not of just length 1, a few modifications would have to be made. Let me know if that's the case and I'll try to help!
int main() {
std::fstream fin("detect_char.txt");
char x;
while (fin >> x) {
if (!isdigit(x)) {
std::cout << "found non-int value = " << x << '\n';
}
}
std::cout << '\n';
return 0;
}
Try reading the tokens into string and explicitly parsing it
ifstream infile("data.txt");
string token;
while (infile >> token) {
try {
double num = stod(token);
cout << num << endl;
}
catch (invalid_argument e) {
cerr << "Has non-numeric entry!" << endl;
}
}
Since it looks like the Asker's end goal is to have a double value for their own nefarious purposes and not simply detect the presence of garbage among the numbers, what the heck. Let's read a double.
double value;
while (in) // loop until failed even after the error handling case
{
if (in >> value) // read a double.
{
std::cout << value; // printing for now. Store as you see fit
}
else // failed to read a double
{
in.clear(); // clear error
std::string junk;
in >> junk; // easiest way I know of to read up to any whitepsace.
// It's kinda gross if the discard is long and the string resizes
}
}
Caveat:
What this can't handle is stuff like 3.14A. This will be read as 3.14 and stop, returning the 3.14 and leave the A for the next read where it will fail to parse and then be consumed and discarded by in >> junk; Catching that efficiently is a bit trickier and covered by William Lee's answer. If the exception handling of stod is deemed to expensive, use strtod and test that the end parameter reached the end of the string and no range errors were generated. See the example in the linked strtod documentation
I am taking a course on C++ and I have been asked to compute the area of a circle by using getline. I was advised to refrain from using cin unless it is really necessary.
Below is my code..
#include <iostream>
#include <string>
int main()
{
std::string question ("Enter the radius: ");
std::string s_radius;
std::cout << question;
getline(std::cin, s_radius);
const double PI = 3.14159;
double n_radius = std::stod(s_radius);
double area = PI * n_radius * n_radius;
std::cout << "The area of the circle = " << area << std::endl;
return 0;
}
Is it really necessary to go through the process of accepting a string as input and convert it to a numeral to perform the calculation?
Is it really necessary to go through the process of accepting a string as input and convert it to a numeral to perform the calculation?
It isn't strictly necessary but it does enforce good habits. Let's assume you are going to ask for 2 numbers from the users and use something like
std::cout << "Enter first number: ";
std::cin >> first_number;
std::cout << "Enter second number: ";
std::cin >> second_number;
For the first number the user enters 123bob. operator>> is going to start reading it in and once it hits b it is going to stop and store 123 into first_number. Now the stream has bob still in it. When it asks for the second number since bob is still in the stream it will fail and second_number will get set to 0. Then the program will continue on and you will get garbage output because you accepted garbage input.
Now, if you read in as a std:string and then convert to what you want it makes it easier to catch these types of errors. getline(std::cin, some_string); would get all of 123bob out of the input buffer so you don't have to worry about having to clean it up. Then using stoi (or any of the stox functions) it will read the valid value of of it. The functions also have a second parameter which is a pointer to a size_t and if you pass it one it will store the position of the first unconverted character in the string and you can use that to tell if the entire input string is valid or not. So, if you had
std::string input;
std::cout << "Enter some number: ";
std::getline(std::cin, input);
std::size_t pos;
double number = std::stod(input, &pos);
if (pos == input.size())
std::cout << "valid input\n";
else
std::cout << "invalid input\n";
Then 1.23bob would cause invalid input to print where 1.23 cause valid input to print. You could even use this in a loop to make sure you get only valid input like
double number;
do
{
std::string input;
std::cout << "Enter some number: ";
std::getline(std::cin, input);
std::size_t pos;
number = std::stod(input, &pos);
if (pos != input.size())
std::cout << "invalid input\n";
else
break;
} while(true)
TL;DR: If you rely on the user to only input valid input eventually you are going to get burned. Reading in as a string and converting offers a consistent way to ensure that you are only getting good input from your user.
I was advised to refrain from using cin unless it is really necessary.
First of all, there is a misunderstanding, because you are still using std::cin. I will interpret your question as using std::cin::operator >> vs getline(std::cin,...). Next I would rather suggest you the opposite: Use operator>> directly if you can and fall back to getline if you have to.
A very contrived example:
Imagine you have integers stored in a file in this (unnecessarily complex) format,
3123212
^-------- number of digits of the first number
^^^----- first number
^---- number of digits of the second number
^^-- second number
You cannot use a bare std::cin >> to read those numbers. Instead you need to read the whole line and then parse the numbers. However, even for this I would highly recommend to provide an overload for operator>>, something along the line of
struct NumbersReadFromStrangeFormat {
std::vector<int> numbers;
};
std::isstream& operator>>(std::istream& in,NumbersReadFromStrangeFormat& num) {
// use getline and parse the numbers from the string
return in;
}
Such that you can again write:
NumbersReadFromStrangeFormat numbers;
std::cin >> numbers;
Conclusion:
Overloading the operator>> is the idomatic way to read something from an input stream. "Using std::cin" or "using getline" are not really alternatives. "Refraining from using std::cin" must be a misunderstanding. If there is no overload of operator>> that already does what you need, you can write your own and nicely hide any getline or other parsing details.
I suppose before even requesting help, I should mention that this one of the various exercises in my Computer Science course to help us further understand how to pull inputs from files and such. Any help is appreciated, whether it's direct "give it all away" answers or pseudocode-type responses. So, here's the prompt.
Write a C++ program that reads lists from a file and reports the name, size, mean if appropriate, and second largest number if appropriate for each list; and reports the list name and sum of the list with the largest sum. The file to be read consists of: a file sentinel, a number not equal to any of the list sentinels... then zero or more of: a list sentinel, a number not equal to any value in this list; the list name, readable as a C++ STL string; zero or more list values separated by whitespace; the list sentinel then the file sentinel.
It includes an example, and here's what the example shows.
31 17.3 FIRST 26.2 -11.2 8.1 17.3 0.0 SECOND 0.0 31
EXPECTED OUTPUT:
First size is 3, mean is 7.7, second largest value is 8.1. Second size is 0. First has the largest sum of the list(s) with 23.1.
Sorry, I know it's all sort of confusing. I'm pretty confused myself, but that's a college course for ya. So, anyways, here is what I've got so far!
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
// loop once per file
// stop if filename string is "end" or "done"
while (true) {
string filename;
cout << "Enter filename or \"end\" to quit: ";
cin >> filename;
if (filename == "end" || filename == "done")
{
break;
}
// open the file
ifstream infile(filename);
cout << "Processing " << filename << ", please wait...\n";
// processing just if file is opened
if (infile)
{
// count the number of lists
int numLists = 0;
int x = 0;
infile >> x;
while (!infile.fail())
{
++numLists;
// read one list
while (x != 31)
{
infile >> x;
}
infile >> x;
}
cout << filename << " contains " << numLists << " lists.\n";
}
}
}
So, my output is working so far concerning determining how many lists are in each file, however, I've tried several things to get the sum and the mean of the lists, but I just don't know how to store the values per list, per file. I've thought about arrays, or vectors, but I just really don't know. I'd appreciate any help anyone could offer. Please!
Here's some code to read the lists in the way the questions asks. I have not tested this code, so apologies for any mistakes, but it does correspond to the question.
ifstream in(filename);
// read the file sentinel first
double file_sentinel;
in >> file_sentinel;
for (;;)
{
// read next sentinel
int sentinel;
in >> sentinel;
// if it's a file sentinel then we are done
if (sentinel == file_sentinel)
break;
// it must be a list sentinel so read the list starting with the name
string name;
in >> name;
// start of list processing
...
for (;;)
{
// read the next item in the list
double item;
in >> item;
// check if the item is actually the list sentinel, if so the we're done with this list
if (item == sentinel)
break;
// now process list item
...
}
// now process whole list
...
}
There's no error checking (not sure if that is necessary) and I've left some blanks for the processing the question asks you to do. Hopefully it will give you a start.
I have a school assignment that I feel should be relatively simply, but I've spent like 5 hours on this part now and can't figure out what I need to do. I'm trying to import 3 integers from each line in a file into 3 different arrays. Each line has an ID number, store number, and quantity ordered. I want to store them in 3 arrays, where the same index would address the integers taken from the same line in the text file.
Recommendations I'm getting from various other places say to use stringstream or a vector, neither of which I have used before and I assume are not needed at this point in the class. My code currently is:
bool loadArrays(const char* fileName, long idArray[], int storeArray[], int qtyArray[], int &count, int maxCells)
{
count = 0;
ifstream fileIn;
fileIn.open("data.txt");
int x = 0;
while ((fileIn.get()) && (x < maxCells))
{
fileIn >> idArray[x] >> storeArray[x] >> qtyArray[x];
count++;
x++;
std::cout << idArray[x] << endl;
}
fileIn.close();
return true;
}
It loops through fine. I'm passing the count variable by reference and printing it out after I run this function it gives me 20. However the cout << idArray[x] line above just displays 0 each time like I'm not importing data correctly?
I'm probably importing the data wrong, which is fine for now, but even so shouldn't I get at least SOMETHING in idArray[0]? The first line of data.txt is '16724 27 134' so idArray[0] should = 16724, yes? I thought the >> will import integers until it meets whitespace, so the numbers being spaced apart like above means that line should go into 3 arrays per line right?
I should point out I'm taking this course online for now to see how I like programming and my teacher effectively doesn't speak english so I'm kind of on my own learning this for now.
You're reading into position x of your array, and printing out position x + 1 (x++ between those lines), which obviously is still empty.
Try to print before incrementing x, like so:
fileIn >> idArray[x] >> storeArray[x] >> qtyArray[x];
std::cout << idArray[x] << endl;
count++;
x++;
You're calling fileIn.get() without using its return value. The get() method extracts on character from the stream and returns it, it doesn't check whether there's still a character in the stream.
You should put the actual input operation inside the loop parameters as it is a much better way of checking if the stream performed a successful read:
while ((fileIn >> idArray[x] >> storeArray[x] >> qtyArray[x]) && (x < maxCells))
Also, as pointed out in the comments, you're incrementing x before printing idArray[x], so it will skip the first index. Put x++ after the insertion.
----------------- EDIT -----------------------
Based on juanchopanza's comment : I edit the title
Based on jrok's comment : I'm using ofstream to write, and ifstream to read.
I'm writing 2 programs, first program do the following tasks :
Has a vector of integers
convert it into array of string
write it in a file
The code of the first program :
vector<int> v = {10, 200, 3000, 40000};
int i;
stringstream sw;
string stringword;
cout << "Original vector = ";
for (i=0;i<v.size();i++)
{
cout << v.at(i) << " " ;
}
cout << endl;
for (i=0;i<v.size();i++)
{
sw << v[i];
}
stringword = sw.str();
cout << "Vector in array of string : "<< stringword << endl;
ofstream myfile;
myfile.open ("writtentext");
myfile << stringword;
myfile.close();
The output of the first program :
Original vector : 10 200 3000 40000
Vector in string : 10200300040000
Writing to File .....
second program will do the following tasks :
read the file
convert the array of string back into original vector
----------------- EDIT -----------------------
Now the writing and reading is fine, thanks to Shark and Jrok,I am using a comma as a separator. The output of first program :
Vector in string : 10,200,3000,40000,
Then I wrote the rest of 2nd program :
string stringword;
ifstream myfile;
myfile.open ("writtentext");
getline (myfile,stringword);
cout << "Read From File = " << stringword << endl;
cout << "Convert back to vector = " ;
for (int i=0;i<stringword.length();i++)
{
if (stringword.find(','))
{
int value;
istringstream (stringword) >> value;
v.push_back(value);
stringword.erase(0, stringword.find(','));
}
}
for (int j=0;j<v.size();i++)
{
cout << v.at(i) << " " ;
}
But it can only convert and push back the first element, the rest is erased. Here is the output :
Read From File = 10,200,3000,40000,
Convert back to vector = 10
What did I do wrong? Thanks
The easiest thing would be to insert a space character as a separator when you're writing, as that's the default separator for operator>>
sw << v[i] << ' ';
Now you can read back into an int variable directly, formatted stream input will do the conversion for you automatically. Use vector's push_back method to add values to it as you go.
Yes, this question is over a year old, and probably completely irrelevant to the original asker, but Google led me here so it might lead others here too.
When posting, please post a complete minimal working example, having to add #include and main and stuff is time better spent helping. It's also important because of your very problem.
Why your second code isn't working is all in this block
for (int i=0;i<stringword.length();i++)
{
if (stringword.find(','))
{
int value;
istringstream (stringword) >> value;
v.push_back(value);
stringword.erase(0, stringword.find(','));
}
}
istringstream (stringword) >> value interprets the data up to the comma as an integer, the first value, which is then stored.
stringword.find(',') gets you the 0-indexed position of the comma. A return value of 0 means that the character is the first character in the string, it does not tell you whether there is a comma in the string. In that case, the return value would be string::npos.
stringword.erase deletes that many characters from the start of the string. In this case, it deletes 10, making stringword ,200,3000,40000. This means that in the next iteration stringword.find(',') returns 0.
if (stringword.find(',')) does not behave as wished. if(0) casts the integer to a bool, where 0 is false and everything else is true. Therefore, it never enters the if-block again, as the next iterations will keep checking against this unchanged string.
And besides all that there's this:
for (int j=0;j<v.size();i++)
{
cout << v.at(i) << " " ;
}
it uses i. That was declared in a for loop, in a different scope.
The code you gave simply doesn't compile, even with the added main and includes. Heck, v isn't even defined in the second program.
It is however not enough, as the for condition stringword.length() is recalculated every loop. In this specific instance it works, because your integers get an extra digit each time, but let's say your input file is 1,2,3,4,:
The loop executes normally three times
The fourth time, stringword is 4, stringword.length() returns 2, but i is already valued 3, so i<stringword.length() is invalid, and the loop exits.
If you want to use the string's length as a condition, but edit the string during processing, store the value before editing. Even if you don't edit the string, this means less calls to length().
If you save length beforehand, in this new scenario that would be 8. However, after 4 loops string is already empty, and it executes the for loop some more times with no effect.
Instead, as we are editing the string to become empty, check for that.
All this together makes for radically different code altogether to make this work:
while (!stringword.empty())
{
int value;
istringstream (stringword) >> value;
v.push_back(value);
stringword.erase(0, stringword.find(',')+1);
}
for (int i = 0; i < v.size(); i++)
{
cout << v.at(i) << " " ;
}
A different way to solve this would have been to not try to find from the start, but from index i onwards, leaving a string of commas. But why stick to messy stuff if you can just do this.
And that's about it.