Problems with storing data into the array - c++

My programs allows user to delete specify records when they input a username. When they pass in the name, it will invoke a method which will store them into a array, and write them back to the file without appending, so as to re-write the file.
But there is some problem during the storing part where my last line of the textfile is not stored properly, and instead it copy from the 2nd last line and copy to the last line with the name included.
Hopefully no1 will get confuse :/. The example of the textfile and data stored inside my array is below which i make bold and italic for clearer picture, and also method for the deleteRec.
This is what my textfile contains.
user;pass;1234;John;1111
user1;pass1;2345;May;2222
user2;pass2;3456;Mary;3333
user3;pass3;4567;Andy;4444
hr;hr;5678;Jonathan;5555
admin;admin;6789;Aili;6666
user10;pass10;7890;eggy;9999
user11;pass11;9807;Mary;7777
This is my output when i run my program to delete.
Data stored in store[] array: user1;pass1;2345;May;2222
Data stored in store[] array: user2;pass2;3456;Mary;3333
Data stored in store[] array: user3;pass3;4567;Andy;4444
Data stored in store[] array: hr;hr;5678;Jonathan;5555
Data stored in store[] array: admin;admin;6789;Aili;6666
Data stored in store[] array: user10;pass10;7890;eggy;9999
***Data stored in store[] array: ;pass10;7890;eggy;9999***
Data stored in store[] array:
bool Employee::deleteRec(string nm)
{
int count;
int i=0;//for looping
ifstream file("login1.txt");
string fusername,empty;
string store[100];//initialize a array to store textfile contents
while (!file.fail())
{
getline(file,fusername,';');// use ; as delimiter
getline(file,empty);// use line end as delimiter, and to skip the rest of the information
string add="";//initialize add string to nothing when it loops
add += fusername+';'+empty; //adds back the username and rest of the line together back
if(fusername!=nm)//to check if the username in textfile do not match the user input name
{
store[i]=add; //store into an array
cout<<"i is: "<<i<<endl;
cout<<"store array[] = "<<store[i]<<endl;
i++;
}
else{}
}
//ofstream pwd2_file ("login1.txt", ios::app); //suppose to user this if im writing to file
for(int x=0;x<i+1;x++)
{
cout<<"Data stored in store[] array: "<<store[x]<<endl;
}
return false;
}

The problem with your loop is that when you reach end of file, your stream will not have failed yet. It only fails on the next read but you are not verifying this.
Therefore your array is containing the last record twice.
That you have an empty string as the first field could be because it set this one empty to read into it, (stream wasn't yet in a failed state) or because there was an empty line at the end of your input file which got read in.
Create a struct for your user and its data, and read in from the stream. If the whole of this read succeeds, you can append to your dataset.
I would suggest you use std::vector for this and push_back().
The correct way to loop is as follows:
struct EmployeeData
{
std::string name;
// fill in members
;
std::istream& operator>>( std::istream& is, EmployeeData& emp )
{
std::getline( is, emp.name(), ';' );
// etc.
return is;
}
std::vector< EmployeeData > emps;
EmployeeData emp;
while( is >> emp )
{
emps.push_back( emp );
}

I'd recommend using a debugger and just figure out what is getting passed over. Looks like an off by one error either in where the output is stored in the array, or when it is rewritten to the file. Would explain your lack of boundary users.

Related

Difference between CSV files which makes different outcome using getline()

I'm writing a function which reads a CSV file using getline() and converts data to the vector of vectors. To test it I've tried to read two files with the same delimiter: one imported from the internet and second exported from R datasets. The first few lines of each looks like:
File1.csv
User ID,Category 1,Category 2,Category 3,Category 4,Category 5,Category 6,Category 7,Category 8,Category 9,Category 10
User 1,0.93,1.8,2.29,0.62,0.8,2.42,3.19,2.79,1.82,2.42
User 2,1.02,2.2,2.66,0.64,1.42,3.18,3.21,2.63,1.86,2.32
User 3,1.22,0.8,0.54,0.53,0.24,1.54,3.18,2.8,1.31,2.5
User 4,0.45,1.8,0.29,0.57,0.46,1.52,3.18,2.96,1.57,2.86
File2.csv
"","Sepal.Length","Sepal.Width","Petal.Length","Petal.Width"
"1",5.1,3.5,1.4,0.2
"2",4.9,3,1.4,0.2
"3",4.7,3.2,1.3,0.2
"4",4.6,3.1,1.5,0.2
However getline() works only for the first one. In second case it simply returns white space. The function performs similar even if I copy single lines from one file to another (of course adding or removing additional colums) -- the rows from file1 will be always properly read while those from file2 never. I've even tried removing " chars, but without much improvement. However switching from comas to '\t' solves the problem.
I'm curious what's the difference between those two files that makes such different outcome?
The source code of my function:
vector<vector<string>> readData(string fileName,int firstLine,char delimeter){
//Open data file
fstream fin;
fin.open(fileName, ios::in);
//Data stored in 2d vector of strings
vector<vector<string>> data;
vector<string> row;
string line,word,temp;
//Read data
int i=0;
while(fin>>temp){
row.clear();
//Read line and store in 'line'
getline(fin,line);
//Don't read first n lines
if (i<firstLine){
i++;
continue;
}
cout<<line<<endl;
//Break words
stringstream s(line);
//Read every column and store in in 'word;
while(getline(s,word,delimeter)){
row.push_back(word);
}
//Append row to the data vector
data.push_back(row);
}
//Close file
fin.close();
return data;
}
The problem is here:
while(fin>>temp){
row.clear();
//Read line and store in 'line'
getline(fin,line);
fin >> temp reads everything till the first space or newline. It is not clear why you do that as only with getline(fin,line) you then try to read the full line and you are not using temp. In the first file fin>>temp consumes only "User", in the second file it consumes the full line, because there are no spaces.
If you look at the read data from the first file you will also notice that the first part of each line is missing.
Tip: Use more meaningful names for your variables. I didn't manage to fully understand your logic, because variables named s and the presence of row and line at the same time causes me headaces.

Merging two text files gives wierd results

I need to merge two text files by putting them in a vector array and then writing them in a new text file.
After merging them.The new file has extra characters.
FE:
f1.txt ("text1")
f2.txt ("text2.")
f12.txt ("text1˙text2.˙W64")
Content of the buffer: "text1 text2. W64"
Here is the code:
int main(){
enum errorcode{FNF,FNC};
vector<char> buffer;
char ime[255];
cin>>ime;//first file
ifstream ud1(ime,ios::in);
if(ud1.is_open()){
while(!ud1.eof())buffer.push_back(ud1.get());
ud1.close();
}
else {cout<<"File not found.";return FNF;}
cin>>ime;//second file
ifstream ud2(ime,ios::in);
if(ud2.is_open()){
while(!ud2.eof())buffer.push_back(ud2.get());
ud2.close();
}
else {cout<<"File not found.";return FNF;}
cin>>ime;//new file
ofstream id(ime,ios::out);
if(id.is_open()){
for(int i=0;i<buffer.capacity();i++)id.put(buffer[i]);
id.close();
}
else {cout<<"File not created.";return FNC;}
return 0;
}
I guess this is because of notepad or files themselves.
Can you please tell me reason for this.
you are using Vector capacity: Returns the size of the storage space currently allocated for the vector, expressed in terms of elements.
You must use vector size: Returns the number of elements in the vector. This is the number of actual objects held in the vector, which is not necessarily equal to its storage capacity.
About the ˙
please look at istream::get return value:
Return Value
The first signature returns the character read, or the end-of-file value (EOF) if no characters are available in the stream (note that in this case, the failbit flag is also set).
So, you could change the loop to this:
while(!ud1.eof()){
int tmpChar = ud1.get();
if( !ud1.eof() )
buffer.push_back(tmpChar);
}

Clarification required regarding Arrays, Vectors and Maps in usage of a C++ Application

I want to know the right algorithm and a container class for my application. I am trying to build one Client-Server communication system where the Server contains group of files (.txt). The file structure (prototype) is like:
A|B|C|D....|Z$(some integer value)#(some integer value). Again the contents of A to Z are a1_a2_a3_a4......aN|b1_b2_b3_b4......bN|......|z1_z2_z3_z4.....zN. So what I wanted to do is when Server application has started, it has to load these files one-by-one and save the contents of each file in a Container class and again the contents of the file into particular variables based on the delimiters i.e.
for (int i=0; i< (Number of files); i++)
{
1) Load the file[0] in Container class[0];
2) Read the Container class[0] search for occurences of delimiters "_" and "|"
3) Till next "|" occurs, save the value occurred at "_" to an array or variable (save it in a buffer)
4) Do this till the file length completes or reaches EOF
5) Next read the second file, save it in Container class[1] and follow the steps as in 2),3) and 4)
}
I want to know if Vector or Map suits my requirement? As I need to search for occurrences of delimiters and push_back them and access while necessity comes.
Can I read whole single file as block and manipulate with the buffer or while file read only using seekg I can push the values to stack? One which will be better and easier to implement? What are the possibilities of using regex?
According to the format of input, and its size, I'd suggest doing something along these lines for reading and parsing the input:
void ParseOneFile (std::istream & inp)
{
std::vector<std::vector<std::string>> data;
int some_int_1 = 0, some_int_2 = 0;
std::string temp;
data.push_back ({});
while (0 == 0)
{
int c = inp.get();
if ('$' == c)
{
data.back().emplace_back (std::move(temp));
break;
}
else if ('|' == c)
{
data.back().emplace_back (std::move(temp));
data.push_back ({});
}
else if ('_' == c)
data.back().emplace_back (std::move(temp));
else
temp += char(c);
}
char sharp;
inp >> some_int_1 >> sharp >> some_int_2;
assert ('#' == sharp);
// Here, you have your data and your two integers...
}
The above function does not return the information it extracts, so you will want to change that. But it does read one of your files into a vector of vector of strings called data and two integers (some_int_1 and some_int_2.) It uses C++11 and does this reading and parsing quite efficiently, both in terms of processing and memory.
And, the above code does not check for any errors and inconsistent formatting in the input file.
Now, for your data structure problem. Since I have no idea about the nature of your data, I can't say for sure. All I can say is that a two-dimensional array and two integers on the side feels like a natural fit for this data. Since you have several files, you can store them all in another dimension of vector (or perhaps in a map, mapping a file name to a data structure like the following:
struct OneFile
{
vector<vector<string>> data;
int i1, i2;
};
vector<OneFile> all_files;
// or...
// map<string, OneFile> all_files;
The above function would fill one instance of the OneFile struct above.
As an example, all_files[0].data[0][0] will be a string referring to data item A0 in the first file, and all_files[7].data[25][3] will be another string referring to data item Z3 in the 8th file.

C++ Reading from a text file into a const char array

I want to read in lines from a text file into a 2-d char array but without the newline character.
Example of .txt:
TCAGC
GTAGA
AGCAG
ATGTC
ATGCA
ACAGA
CTCGA
GCGAC
CGAGC
GCTAG
...
So far, I have:
ifstream infile;
infile.open("../barcode information.txt");
string samp;
getline(infile,samp,',');
BARCLGTH = samp.length();
NUMSUBJ=1;
while(!infile.eof())
{
getline(infile,samp,',');
NUMSUBJ++;
}
infile.close(); //I read the file the first time to determine how many sequences
//there are in total and the length of each sequence to determine
//the dimensions of my array. Not sure if there is a better way?
ifstream file2;
file2.open("../barcode information.txt");
char store[NUMSUBJ][BARCLGTH+1];
for(int i=0;i<NUMSUBJ;i++)
{
for(int j=0;j<BARCLGTH+1;j++)
{
store[i][j] = file2.get();
}
}
However, I do not know how to ignore the newline character. I want the array to be indexed so that I can access a sequence with the first index and then a specific char within that sequence with the second index; i.e. store[0][0] would give me 'T', but I do not want store[0][5] to give me '\n'.
Also, as an aside, store[0][6], which I think should be out of bounds since BARCLGTH is 5, returns 'G',store[0][7] returns 'T',store[0][8] returns 'A', etc. These are the chars from the next line. Alternatively, store[1][0],store[1][1], and store[1][2] also return the same values. Why does the first set return values, shouldn't they be out of bounds?
As you're coding in C++, you could do like this instead:
std::vector<std::string> barcodes;
std::ifstream infile("../barcode information.txt");
std::string line;
while (std::getline(infile, line))
barcodes.push_back(line);
infile.close();
After this the vector barcodes contains all the contents from the file. No need for arrays, and no need to count the number of lines.
And as both vectors and strings can be indexed like arrays, you can use syntax such as barcodes[2][0] to get the first character of the third entry.

Initializing a Vector of Objects from a .txt file

#include<iostream>
#include<vector>
#include<fstream>
#include "stock.h"
int main(){
double balance =0, tempPrice=0;
string tempStr;
vector < Stock > portfolio;
typedef vector<Stock>::iterator StockIt;
ifstream fileIn( "Results.txt" );
for(StockIt i = portfolio.begin(); i != portfolio.end(); i++)
{
while ( !fileIn.eof( ))
{
getline(fileIn,tempStr);
i->setSymbol(tempStr);
fileIn >> tempPrice;
i->setPrice(tempPrice);
getline(fileIn,tempStr);
i->setDate(tempStr);
}
fileIn.close();
}
for(StockIt i = portfolio.begin(); i != portfolio.end(); i++){
cout<<i->getSymbol() <<endl;
cout<<i->getPrice() <<endl;
cout<<i->getDate() <<endl;
}
return 0;
}
Sample text file, Results.txt:
GOOG 569.964 11/17/2010
MSFT 29.62 11/17/2010
YHOO 15.38 11/17/2010
AAPL 199.92 11/17/2010
Now obviously, I want this program to create a vector of Stock Objects which has the appropriate set/get functionality for object: Stock(string, double, string).
Once that is done, I want to print out each individual member of each Object in the vector.
One thing that boggles my mind about fstream, is how can it decipher spaces and end of lines, and intelligently read strings/ints/doubles and place them into the appropriate data type? Maybe it can't...and I have to add an entirely new functionality?
now it would seem that I'm not actually creating a new object for each iteration of the loop? I think would need to do something along the lines of:
portfolio.push_back(new Stock(string, double, string));? I'm just not entirely sure how to get to that point.
Also, this code should be interchangeable with std::list as well as std::vector. This program compiles without error, however, there is zero output.
First of all, iterating over the vector only makes sense when it isn't empty. So remove the line:
for(StockIt i = portfolio.begin(); i != portfolio.end(); i++)
because otherwise the contents of this loop will never be executed.
Second, you have problems with your input reading: you use getline for the first field, which would read the values of all 3 fields on the line into the tempStr variable.
Third, you shouldn't use while(!fileIn.eof()) - the eof function only returns true after you tried to read past the end of the file. Instead, use:
while (fileIn >> symbol >> price >> date) {
//here you should create a Stock object and call push_back on the vector.
}
This will read the three fields, which are separated by spaces.
Few issues in your code.
the first for loop runs on an empty portfolio vector, as the vector is not initialized (no objects are being pushed to it) the begin() and end() are the same.
you should read line by line from the fstream until EOF, then push objects to the vector.
each line you read, you should split (tokenize) into the 3 parts, and create a new Stock object to be pushed to the vector.
Another side feedback, whenever using an stl iterator, use ++itr on for loops, it will run much more fast