I have an issue that I'm basically baffled by. To start off, I have two global arrays - trustArray[] and fashionArray[]. Here is the function that fills the trustArray:
void getTrust()
{
string line;
int reachedTrust=0;
int numberOfTrustsRecorded=0;
ifstream myfile ("BuyingJeans.Hw5 (1).csv");
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line,',');
//int found=line.find("Like-Purchase");
if (line=="Trust-Like"){
reachedTrust=1;
getline (myfile,line,',');
}
if(reachedTrust==1){
if(numberOfTrustsRecorded <6){
double testValue = atof(line.c_str());
trustArray[numberOfTrustsRecorded] = testValue;
numberOfTrustsRecorded++;
}
}
}
myfile.close();
}
else
cout << "Unable to open file";
}
For some reason, the atof() in this function is changing two of the values in fashionArray[]. If I change the atof() to say, an atoi(), the problem no longer occurs. Here is the method that fills the array that is being changed (fashionArray[]):
void getFashion(){
string line;
int reachedFashion=0;
int numberOfFashionsRecorded=0;
ifstream myfile ("BuyingJeans.Hw5 (1).csv");
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line,',');
if (line=="Like-Fash -->"){
reachedFashion=1;
getline (myfile,line,',');
//cout<<line<<endl;
//getchar();
}
if(reachedFashion==1){
if(numberOfFashionsRecorded <6){
fashionArray[numberOfFashionsRecorded] = atoi(line.c_str());
numberOfFashionsRecorded++;
}
}
}
myfile.close();
}
else cout << "Unable to open file";
}
Here is the main method that calls these two methods:
int main () {
getFashion();
getTrust();
for(int x=0; x<6;x++)
cout<<fashionArray[x]<<endl;
getchar();
return 0;
}
The first two values of fashionArray end up being changed to some ridiculously large negative and positive integers. One interesting thing is that if I reverse the order in which the two methods are called in the main() method, the issue no longer occurs. Anyone have any idea what could be causing this?
I think you are writing beyond trustArray and into fashionArray. You have not provided initialisation code (please do), but I suppose it looks something like this :
float trustArray[N];
float fashionArray[N];
With N equal to some positive integer. My guess is that in your case, N=5.
In your loop, you test that numberOfTrustsRecorded < 6. Values of numberOfTrustsRecorded that will pass that test are 0, 1, 2, 3, 4, 5. That's six (6) floats to fit in a array of 5. Writing numberOfTrustRecorded[5] will overwrite memory. Change your test or increase your buffer size to 6.
Why aren't you seeing valid values in fashionArray[0] ? I don't know. Maybe your compiler aligned the fashionArray memory buffer so the overwrite starts in unused memory, leaving you with half the bits required to make a IEEE float. Whatever bits are in memory make up a random float. A memory dump would show the problem.
Why is running the method in reverse order works ? Probably that the bug is still there, but running getFashion() second cleans the mess left by getTrust(). The memory you overwrite is yours, so as long as you don't try to make sense of it, nobody complains. Initialize fashionArray[0] = 0.0, run getTrust() and look at fashionArray[0] before running getFashion(). You will probably see the random floats.
Related
I am attempting to read data from a text file into an array of structures. The first iteration of the for-loop reads and displays everything correctly until it reaches the Boolean value, and every iteration after that does not display as expected. Is the bool value at the end causing the entire rest of the file to be read incorrectly? Or perhaps an issue stemming from getline?
int main()
{
groceryProduct inventoryDatabase[25];
ifstream fin("inventory.txt");
if (!fin)
{
cout << "File could not be located.";
}
string itemName;
for (int index = 0; index < 25; index++)
{
getline(fin, inventoryDatabase[index].itemName, '\n');
fin >> inventoryDatabase[index].itemNumber;
fin >> inventoryDatabase[index].itemPrice;
fin >> inventoryDatabase[index].membershipPrice;
fin >> inventoryDatabase[index].payByWeight;
cout << inventoryDatabase[index].itemName << endl;
cout << inventoryDatabase[index].itemNumber << endl;
cout << inventoryDatabase[index].itemPrice << endl;
cout << inventoryDatabase[index].membershipPrice << endl;
cout << inventoryDatabase[index].payByWeight << endl;
}
return 0;
};
The structure:
struct groceryProduct
{
double itemPrice;
double membershipPrice;
double itemWeight;
int itemQuantity;
string itemNumber;
string itemName;
bool payByWeight;
};
The output:
Apple
P0000
0.85
0.8
204 (expected output of 'false' instead of 204)
Output for every iteration of loop after first iteration:
-9.25596e+61
-9.25596e+61
204
Thank you, and please let me know if you require any more information.
File:
Apple
P0000
.85
.80
false
Orange
P0001
.95
.85
false
Lemon
P0002
.65
.60
false
You need to tell your stream that the bool values are text with
fin >> boolalpha >> inventoryDatabase[index].payByWeight
You're seeing garbage data after the first bool input because failbit gets set in the stream and no further inputs will work until it is reset. This results in you array's data staying uninitialized.
Here are a couple of things I see that may be causing your problem.
1) An array is not "magically filled" with data. You have an uninitialized array, meaning that the data inside of it does not yet exist. At all.
What you have to do to remedy this is to add a new instance of the struct to the array at the start of each loop iteration.
How'd I spot that? Good rule of thumb: if it's weird, it's memory-related. Make sure you've initialized everything.
2) I've seen weird things happen when you use getline and << next to each other. Is there a particular reason you are using getline over <<?
(I would need to re-research how to work around that weirdness. I used to hit it a lot in my C++ class way back when.)
3) What 1201ProgramAlarm said is absolutely correct.
Side note: Do NOT get into the habit of throwing double around because "I want to be able to arbitrarily throw a large value in there." It's a bad habit that wastes space, as double is twice as large as float.
Learn the difference between float and double - you will almost never need double outside of scientific situations, because it is for numbers with a LOT of decimal places. (That's oversimplifying it.) If you're using double over float all the time, you're using twice the memory you need - 32 bits per variable extra, in fact. It adds up. (And people wonder why modern programs need 8GB of RAM to do the same thing as their 100MB-RAM-using predecessors...)
Prices always have two (rarely three) decimal places, so float should fit that perfectly in all cases. Same with weights.
I'm really stuck with this problem I'm having for reading rows and columns from a text file. We're using text files that our prof gave us. I have the functionality running so when the user in puts "numrows (file)" the number of rows in that file prints out.
However, every time I enter the text files, it's giving me 19 for both. The first text file only has 4 rows and the other one has 7. I know my logic is wrong, but I have no idea how to fix it.
Here's what I have for the numrows function:
int numrows(string line) {
ifstream ifs;
int i;
int row = 0;
int array [10] = {0};
while (ifs.good()) {
while (getline(ifs, line)) {
istringstream stream(line);
row = 0;
while(stream >>i) {
array[row] = i;
row++;
}
}
}
}
and here's the numcols:
int numcols(string line) {
int col = 0;
int i;
int arrayA[10] = {0};
ifstream ifs;
while (ifs.good()) {
istringstream streamA(line);
col = 0;
while (streamA >>i){
arrayA[col] = i;
col++;
}
}
}
edit: #chris yes, I wasn't sure what value to return as well. Here's my main:
int main() {
string fname, line;
ifstream ifs;
cout << "---- Enter a file name : ";
while (getline(cin, fname)) { // Ctrl-Z/D to quit!
// tries to open the file whose name is in string fname
ifs.open(fname.c_str());
if(fname.substr(0,8)=="numrows ") {
line.clear();
for (int i = 8; i<fname.length(); i++) {
line = line+fname[i];
}
cout << numrows (line) << endl;
ifs.close();
}
}
return 0;
}
This problem can be more easily solved by opening the text file as an ifstream, and then using std::get to process your input.
You can try for comparison against '\n' as the end of line character, and implement a pair of counters, one for columns on a line, the other for lines.
If you have variable length columns, you might want to store the values of (numColumns in a line) in a std::vector<int>, using myVector.push_back(numColumns) or similar.
Both links are to the cplusplus.com/reference section, which can provide a large amount of information about C++ and the STL.
Edited-in overview of possible workflow
You want one program, which will take a filename, and an 'operation', in this case "numrows" or "numcols". As such, your first steps are to find out the filename, and operation.
Your current implementation of this (in your question, after editing) won't work. Using cin should however be fine. Place this earlier in your main(), before opening a file.
Use substr like you have, or alternatively, search for a space character. Assume that the input after this is your filename, and the input in the first section is your operation. Store these values.
After this, try to open your file. If the file opens successfully, continue. If it won't open, then complain to the user for a bad input, and go back to the beginning, and ask again.
Once you have your file successfully open, check which type of calculation you want to run. Counting a number of rows is fairly easy - you can go through the file one character at a time, and count the number that are equal to '\n', the line-end character. Some files might use carriage-returns, line-feeds, etc - these have different characters, but are both a) unlikely to be what you have and b) easily looked up!
A number of columns is more complicated, because your rows might not all have the same number of columns. If your input is 1 25 21 abs 3k, do you want the value to be 5? If so, you can count the number of space characters on the line and add one. If instead, you want a value of 14 (each character and each space), then just count the characters based on the number of times you call get() before reaching a '\n' character. The use of a vector as explained below to store these values might be of interest.
Having calculated these two values (or value and set of values), you can output based on the value of your 'operation' variable. For example,
if (storedOperationName == "numcols") {
cout<< "The number of values in each column is " << numColsVal << endl;
}
If you have a vector of column values, you could output all of them, using
for (int pos = 0; pos < numColsVal.size(); pos++) {
cout<< numColsVal[pos] << " ";
}
Following all of this, you can return a value from your main() of 0, or you can just end the program (C++ now considers no return value from main to a be a return of 0), or you can ask for another filename, and repeat until some other method is used to end the program.
Further details
std::get() with no arguments will return the next character of an ifstream, using the example code format
std::ifstream myFileStream;
myFileStream.open("myFileName.txt");
nextCharacter = myFileStream.get(); // You should, before this, implement a loop.
// A possible loop condition might include something like `while myFileStream.good()`
// See the linked page on std::get()
if (nextCharacter == '\n')
{ // You have a line break here }
You could use this type of structure, along with a pair of counters as described earlier, to count the number of characters on a line, and the number of lines before the EOF (end of file).
If you want to store the number of characters on a line, for each line, you could use
std::vector<int> charPerLine;
int numberOfCharactersOnThisLine = 0;
while (...)
{
numberOfCharactersOnThisLine = 0
// Other parts of the loop here, including a numberOfCharactersOnThisLine++; statement
if (endOfLineCondition)
{
charPerLine.push_back(numberOfCharactersOnThisLine); // This stores the value in the vector
}
}
You should #include <vector> and either specific std:: before, or use a using namespace std; statement near the top. People will advise against using namespaces like this, but it can be convenient (which is also a good reason to avoid it, sort of!)
I'm working on a C++ program (C++ 98). It reads a text file with lots of lines (10000 lines). These are tab separated values and then I parse it into Vector of Vector objects. However It seems to work for some files (Smaller) but One of my files gives me the following error (this file has 10000 lines and it's 90MB). I'm guessing this is a memory related issue?
Can you please help me?
Error
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Abort
Code
void AppManager::go(string customerFile) {
vector<vector<string> > vals = fileReader(customerFile);
for (unsigned int i = 0; i < vals.size();i++){
cout << "New One\n\n";
for (unsigned int j = 0; j < vals[i].size(); j++){
cout << vals[i][j] << endl;
}
cout << "End New One\n\n";
}
}
vector<vector<string> > AppManager::fileReader(string fileName) {
string line;
vector<vector<string> > values;
ifstream inputFile(fileName.c_str());
if (inputFile.is_open()) {
while (getline(inputFile,line)) {
std::istringstream iss(line);
std::string val;
vector<string> tmp;
while(std::getline(iss, val, '\t')) {
tmp.push_back(val);
}
values.push_back(tmp);
}
inputFile.close();
}
else {
throw string("Error reading the file '" + fileName + "'");
}
return values;
}
There's nothing wrong with your code, you're simply running on a platform likely with small memory limits, likely an old compiler and likely an old C++ library. It all conspires against you. You'll have to micro-optimize :(
Here's what you can do, starting with lowest hanging fruit first:
Do a dry run through the file, just counting the lines. Then values.resize(numberOfLines) , seek to the beginning and only then read the values. Of course you won't be using values.push_back anymore, merely values[lineNumber] = tmp. Resizing the values vector as you add to it may more than double the amount of memory your process needs on a temporary basis.
At the end of the line, do tmp.resize(tmp.size() - it'll shrink the vector to just fit the data.
You can reduce overheads in the existing code by storing all the values in one vector.
If each line has a different number of elements, but you access them sequentially later on, you can store an empty string as an internal delimiter, it may have lower overhead than the vector.
If each line has same number of values, then splitting them by lines adds unnecessary overhead - you know the index of the first value in each line, it's simply lineNumber * valuesPerLine, where first line has number 0.
Memory-map the file. Store the beginning and end of each word in a structure element of a vector, perhaps with a line number as well if you need it split up in lines.
I was wondering if anyone could help me with my code (C++)? This is the function that will not work. When I run it, Windows pops up and says "This program has stopped working." Here is the code that will not run (it's a little long, but the program is proprietary and I appreciate any help):
void ClassName::loadGrades(){
string temp;
int count=0;
ifstream labErnIn("labGradesErn.txt");
if(labErnIn.is_open()){
count=0;
while(labErnIn.good()){
getline(labErnIn, temp);
float tempF = ::atof(temp.c_str());
labGradesErn[count] = tempF;
if(labGradesErn[count]==0 && count==0) labGradesErn[count]=-1;
count++;
}
labGradesErn[count-1]=-1;
labErnIn.close();
}
else{
cout << "Unable to open file labGradesErn.txt" << endl;
}
// I repeat this for three other sections, same code, different var names.
// From 'ifstream...' to 'Unable to open file'
}
All variables not declared in this function are declared elsewhere.
Thanks, I really appreciate any and all help!
If labGradesErn is a fixed array, you risk an array overrun sooner or later. If it is a std::vector, you should use push_back() to append elements, because increasing the index doesn't increase the vector.
Another point is your while loop. You don't test if getline succeeds and you should loop on getline instead of good
while (getline(labErnIn, temp)) {
...
}
I have numerical text data lines ranging between 1mb - 150 mb in size, i need to write lines of numbers related to heights, for example: heights=4 , new text must include lines: 1,5,9,13,17,21.... consequentially.
i have been trying to find a way to do this for a while now, tried using a list instead of vector which ended up with compilation errors.
I have cleaned up the code as advised. It now writes all lines sample2 text, all done here. Thank you all
I am open to method change as long as it delivers what i need, Thank you for you time and help.
following is what i have so far:
#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <vector>
using namespace std;
int h,n,m;
int c=1;
int main () {
cout<< "Enter Number Of Heights: ";
cin>>h;
ifstream myfile_in ("C:\\sample.txt");
ofstream myfile_out ("C:\\sample2.txt");
string line;
std::string str;
vector <string> v;
if (myfile_in.is_open()) {
myfile_in >> noskipws;
int i=0;
int j=0;
while (std::getline(myfile_in, line)) {
v.push_back( line );
++n;
if (n-1==i) {
myfile_out<<v[i]<<endl;
i=i+h;
++j;
}
}
cout<<"Number of lines in text file: "<<n<<endl;
}
else cout << "Unable to open file(s) ";
cout<< "Reaching here, Writing one line"<<endl;
system("PAUSE");
return 0;
}
You need to use seekg to set the position at the beginning of the file, once you have read it (you have read it once, to count the lines (which I don't think you actually need, as this size is never used, at least in this piece of code)
And what is the point if the inner while? On each loop, you have
int i=1;
myfile_out<<v[i]; //Not writing to text
i=i+h;
So on each loop, i gets 1, so you output the element with index 1 all the time. Which is not the first element, as indices start from 0. So, once you put seekg or remove the first while, your program will start to crash.
So, make i start from 0. And get it out of the two while loops, right at the beginning of the if-statement.
Ah, the second while is also unnecessary. Leave just the first one.
EDIT:
Add
myfile_in.clear();
before seekg to clear the flags.
Also, your algorithm is wrong. You'll get seg fault, if h > 1, because you'll get out of range (of the vector). I'd advise to do it like this: read the file in the while, that counts the lines. And store each line in the vector. This way you'll be able to remove the second reading, seekg, clear, etc. Also, as you already store the content of the file into a vector, you'll NOT lose anything. Then just use for loop with step h.
Again edit, regarding your edit: no, it has nothing to do with any flags. The if, where you compare i==j is outside the while. Add it inside. Also, increment j outside the if. Or just remove j and use n-1 instead. Like
if ( n-1 == i )
Several things.
First you read the file completely, just to count the number of lines,
then you read it a second time to process it, building up an in memory
image in v. Why not just read it in the first time, and do everything
else on the in memory image? (v.size() will then give you the number
of lines, so you don't have to count them.)
And you never actually use the count anyway.
Second, once you've reached the end of file the first time, the
failbit is set; all further operations are no-ops, until it is reset.
If you have to read the file twice (say because you do away with v
completely), then you have to do myfile_in.clear() after the first
loop, but before seeking to the beginning.
You only test for is_open after having read the file once. This test
should be immediately after the open.
You also set noskipws, although you don't do any formatted input
which would be affected by it.
The final while is highly suspect. Because you haven't done the
clear, you probably never enter the loop, but if you did, you'd very
quickly start accessing out of bounds: after reading n lines, the size
of v will be n, but you read it with index i, which will be n * h.
Finally, you should explicitly close the output file and check for
errors after the close, just in case.
It's not clear to me what you're trying to do. If all you want to do is
insert h empty lines between each existing line, something like:
std::string separ( h + 1, '\n' );
std::string line;
while ( std::getline( myfile_in, line ) ) {
myfile_out << line << separ;
}
should do the trick. No need to store the complete input in memory.
(For that matter, you don't even have to write a program for this.
Something as simple a sed 's:$:\n\n\n\n:' < infile > outfile would do
the trick.)
EDIT:
Reading other responses, I gather that I may have misunderstood the
problem, and that he only wants to output every h-th line. If this is
the case:
std::string line;
while ( std::getline( myfile_in, line ) ) {
myfile_out << line << '\n';
for ( int count = h - 1; h > 0; -- h ) {
std::getline( myfile_in, line );
// or myfile_in.ignore( INT_MAX, '\n' );
}
}
But again, other tools seem more appropriate. (I'd follow thiton's
suggestion and use AWK.) Why write a program in a language you don't
know well when tools are already available to do the job.
If there is no absolutely compelling reason to do this in C++, you are using the wrong programming language for this. In awk, your whole program is:
{ if ( FNR % 4 == 1 ) print; }
Or, giving the whole command line e.g. in sh to filter lines 1,5,9,13,...:
awk '{ if ( FNR % 4 == 1 ) print; }' a.txt > b.txt