I am quite new to C++ and am trying to work out how to write a record in the format of this structure below to a text file:
struct user {
int id;
char username [20];
char password [20];
char name [20];
char email [30];
int telephone;
char address [70];
int level;
};
So far, I'm able to write to it fine but without an incremented id number as I don't know how to work out the number of records so the file looks something like this after I've written the data to the file.
1 Nick pass Nick email tele address 1
1 user pass name email tele address 1
1 test test test test test test 1
1 user pass Nick email tele addy 1
1 nbao pass Nick email tele 207 1
Using the following code:
ofstream outFile;
outFile.open("users.dat", ios::app);
// User input of data here
outFile << "\n" << 1 << " " << username << " " << password << " " << name << " "
<< email << " " << telephone << " " << address << " " << 1;
cout << "\nUser added successfully\n\n";
outFile.close();
So, how can I increment the value for each record on insertion and how then target a specific record in the file?
EDIT: I've got as far as being able to display each line:
if (inFile.is_open())
{
while(!inFile.eof())
{
cout<<endl;
getline(inFile,line);
cout<<line<<endl;
}
inFile.close();
}
What you have so far is not bad, except that it cannot handle cases where there is space in your strings (for example in address!)
What you are trying to do is write a very basic data base. You require three operations that need to be implemented separately (although intertwining them may give you better performance in certain cases, but I'm sure that's not your concern here).
Insert: You already have this implemented. Only thing you might want to change is the " " to "\n". This way, every field of the struct is in a new line and your problem with spaces are resolved. Later when reading, you need to read line by line
Search: To search, you need to open the file, read struct by struct (which itself consists of reading many lines corresponding to your struct fields) and identifying the entities of your interest. What to do with them is another issue, but simplest case would be to return the list of matching entities in an array (or vector).
Delete: This is similar to search, except you have to rewrite the file. What you do is basically, again read struct by struct, see which ones match your criteria of deletion. You ignore those that match, and write (like the insert part) the rest to another file. Afterwards, you can replace the original file with the new file.
Here is a pseudo-code:
Write-entity(user &u, ofstream &fout)
fout << u.id << endl
<< u.username << endl
<< u.password << endl
<< ...
Read-entity(user &u, ifstream &fin)
char ignore_new_line
fin >> u.id >> ignore_new_line
fin.getline(u.username, 20);
fin.getline(u.password, 20);
...
if end of file
return fail
Insert(user &u)
ofstream fout("db.dat");
Write-entity(u, fout);
fout.close();
Search(char *username) /* for example */
ifstream fin("db.dat");
user u;
vector<user> results;
while (Read-entity(u))
if (strcmp(username, u.username) == 0)
results.push_back(u);
fin.close();
return results;
Delete(int level) /* for example */
ifstream fin("db.dat");
ofstream fout("db_temp.dat");
user u;
while (Read-entity(u))
if (level != u.level)
Write-entity(u, fout);
fin.close();
fout.close();
copy "db_temp.dat" to "db.dat"
Side note: It's a good idea to place the \n after data has been written (so that your text file would end in a new line)
Using typical methods at least you will need to use fix size records if you want to have random access when reading the file so say you have 5 characters for name it will be stored as
bob\0\0
or whatever else you use to pad, this way you can index with record number * record size.
To increment the index you in the way you are doing you will need to the read the file to find the high existing index and increment it. Or you can load the file into memory and append the new record and write the file back
std::vector<user> users=read_dat("file.dat");
user user_=get_from_input();
users.push_back(user_);
then write the file back
std::ofstream file("file.dat");
for(size_t i=0; i!=users.size(); ++i) {
file << users.at(i);
//you will need to implement the stream extractor to do this easily
}
I suggest to wrap the file handler into a Class, and then overload the operator >> and << for your struct, with this was you will control the in and out.
For instance
struct User{
...
};
typedef std::vector<User> UserConT;
struct MyDataFile
{
ofstream outFile;
UserConT User_container;
MyDataFile(std::string const&); //
MyDataFile& operator<< (User const& user); // Implement and/or process the record before to write
MyDataFile& operator>> (UserConT & user); // Implement the extraction/parse and insert into container
MyDataFile& operator<< (UserConT const & user); //Implement extraction/parse and insert into ofstream
};
MyDataFile& MyDataFile::operator<< (User const& user)
{
static unsigned myIdRecord=User_container.size();
myIdRecord++;
outFile << user.id+myIdRecord << ....;
return *this;
}
int main()
{
MydataFile file("data.dat");
UserConT myUser;
User a;
//... you could manage a single record
a.name="pepe";
...
file<<a;
..//
}
A .Dat file is normally a simple text file itself that can be opened with notepad . So , you can simply read the Last Line of the file , read it , extract the first character , convert it into integer . THen increment the value and be done .
Some sample code here :
#include <iostream.h>
#include <fstream.h>
using namespace std;
int main(int argc, char *argv[])
{
ifstream in("test.txt");
if(!in) {
cout << "Cannot open input file.\n";
return 1;
}
char str[255];
while(in) {
in.getline(str, 255); // delim defaults to '\n'
//if(in) cout << str << endl;
}
// Now str contains the last line ,
if ((str[0] >=48) || ( str[0] <=57))
{
int i = atoi(str[0]);
i++;
}
//i contains the latest value , do your operation now
in.close();
return 0;
}
Assuming your file format doesn't not need to be human readable.
You can write the struct out to file such as.
outFile.open("users.dat", ios::app | ios::binary);
user someValue = {};
outFile.write( (char*)&someValue, sizeof(user) );
int nIndex = 0;
user fetchValue = {};
ifstream inputFile.open("user.data", ios::binary);
inputFile.seekg (0, ios::end);
int itemCount = inputFile.tellg() / sizeof(user);
inputFile.seekg (0, ios::beg);
if( nIndex > -1 && nIndex < itemCount){
inputFile.seekg ( sizeof(user) * nIndex , ios::beg);
inputFile.read( (char*)&fetchValue, sizeof(user) );
}
The code that writes to the file is a member function of the user struct?
Otherwise I see no connection with between the output and the struct.
Possible things to do:
write the id member instead of 1
use a counter for id and increment it at each write
don't write the id and when reading use the line number as id
Related
I have just a couple issues here with my code. It works but I'm not advanced enough to do what I want to yet. Nor do I know how to word it for a google search. I have a Blackjack game that I'm doing and want to know how to edit certain lines of the file depending on user input. Simpler, I want a user to be able to open the game and start with their balance from the last time they were playing(a save and load feature). My issues are the balance and the username are on the same line in the text file (purposefully) and I want to assign the variables to those in the text file. I know I'm on the right track, I just dont know where to go from here. Thanks for the help in advance. If I broke a rule of posting, I'm sorry.
input username
if username is present in file
edit balance on leave
if username isnt present in file
create new user
Here is my code for the load function:
void load(userAcc user1)
{
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
string word;
for (int x = 0; in >> word; x++);
{
user1.name = word;
user1.balance = word;
}
cout << user1.name << endl;
cout << user1.balance << endl;
in.close();
}
else
cout << "Cannot open a file";
}
void save(userAcc user1)
{
user1.balance = "1000";
cout << "Enter a username: ";
cin >> user1.name;
ofstream out;
out.open("Balances.txt", ios_base::app);
if (out.is_open())
{
out << user1.name << " " << user1.balance << endl;
out.close();
}
else
cout << "Cannot open a file";
}
In
for (int x = 0; in >> word; x++);
remove the trailing ;. It ends the statement before the body of the for loop, separating the two. The for spins around doing nothing but reading the file until it ends and incrementing the unused variable x and the following code block will be run exactly once, storing whatever is in word (and since the loop will exit when the read into word fails, what's in word will depend on the C++ Standard version the the compiler's been set to use) into user1.
Once the ; is removed, the for loop will read into word until no more words can be read from the file. Every word read is copied into the same userAcc writing over the previous word. When the file hits the end in >> word will fail and the loop will exit. The last word in the file will then be printed out, all other words having been overwritten.
Naïve fixing of this would look something like
void load(userAcc user1)
{
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
while (in >> user1.name // read name in from file
>> user1.balance) // read balance in from file
{ // loop will exit when it cannot read a name and a balance from the file
// for now we're just printing out what's read from the file.
cout << user1.name << endl << user1.balance << endl;
}
// in.close(); not needed. File will automatically close when in goes out of scope.
}
else
cout << "Cannot open a file";
}
But we probably want to do more than print out all of the users in the file, so let's put them into a convenient resizable container like std::vector.
vector<userAcc> load() // takes no parameters, returns list of accounts
{
vector<userAcc> accounts;
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
userAcc user1; // account we can read into
while (in >> user1.name >> user1.balance)
{
accounts.push_back(user1); // store account
}
}
else
cout << "Cannot open a file";
return accounts; // hand accounts read back to caller.
}
Use of the function would be something like
vector<userAcc> accounts = load();
The save function looks pretty much good-to-go as written.
It may be a easy problem...
the method read in stdin or file read in text has been proved be right. Things go wrong in binary read.
Here I have a class named Laptop and a file named laptop.txt, which is written by the code followed. I have reloaded the >> and <<
using namespace std;
class Laptop
{
private:
string brand;
string cpu;
string ram;
string disk;
int reserve;
public:
Laptop() {}
Laptop(string aBrand, string aCpu, string aRam, string aDisk, int aReserve)
{
this->brand = aBrand;
this->cpu = aCpu;
this->ram = aRam;
this->disk = aDisk;
this->reserve = aReserve;
}
friend ostream &operator<<(ostream &os, const Laptop &laptop)
{
os << laptop.brand << " " << laptop.cpu
<< " " << laptop.ram << " " << laptop.disk << " " << laptop.reserve;
return os;
}
friend istream &operator>>(istream &is, Laptop &laptop)
{
is >> laptop.brand >> laptop.cpu >> laptop.ram >> laptop.disk >> laptop.reserve;
return is;
}
};
int main()
{
fstream file("laptop.txt", ios::out | ios::binary);
vector<Laptop> laptops;
Laptop aLaptop;
for (int i = 0; i < 5; i++)
{
cin >> aLaptop;
laptops.push_back(aLaptop);
}
for (vector<Laptop>::iterator i = laptops.begin(); i != laptops.end(); i++)
{
file.write((char *)(&i), sizeof(*i));
}
return 0;
}
But things doesn't go right in binary read. Here comes to the exception from class Laptop when I try to push aLaptop to the vector. I really don't know why. It's horrible.
int main()
{
fstream file("laptop.txt", ios::in);
vector<Laptop> laptops;
Laptop aLaptop;
while (file.peek() != EOF)
{
file.read((char *)(&aLaptop), sizeof(aLaptop));
laptops.push_back(aLaptop);
}
return 0;
}
enter image description here
You have a class consisting of four strings and an int, and you cast a pointer to it to a character pointer and try to read it in binary mode from a text file.
A string consists of a length and a pointer. The pointer is pointing to a variable-sized block of memory containing characters. sizeof returns the size of the length and the pointer, but not the block of characters. So when you read the file, you get the length and the pointer, but not the characters that compose the string. The pointer is thus pointing to garbage.
The correct way to do this is either:
If you know that the string will never contain a null character, read characters, appending them to the string, until you get a null character or the end of file.
Read the length of the string, then read that many characters. If you hit the end of file in the middle of this, throw.
You will also need a function to write the string in the same way.
Reading an integer from a binary file works, as long as the integer was written binarily. For portability, it's better to read and write integers in a consistent endianness.
I can get my file to load 1 full struct. But when I try to iterate through the file, i have an endless loop and no data is loaded. I have attached my code and the flat file.
#include <iostream>
#include <string.h>
#include <vector>
#include <fstream>
using namespace std;
typedef struct video_items{
string Title;
int YOP;
string Category;
float Gross;
}video;
void LoadMovies (vector<video> &v);
void SearchMovies (vector <video> &v, string s);
int main()
{
vector <video> v;
LoadMovies(v);
cout<<"Total number of movies: "<<v.size()<<endl;
for (unsigned int i=0; i<v.size(); ++i) {
cout<<"----------------------------------------------------"<<endl;
cout<<v[i].Title<<endl;
cout<<"Category: "<<v[i].Category<<endl;
cout<<"Year of Publication: "<<v[i].YOP<<endl;
cout<<"Gross: "<<v[i].Gross<<endl;
cout<<"----------------------------------------------------"<<endl<<endl;
}
string WantMovie;
cout<<"Please type in what movie you want."<<endl;
cin>>WantMovie;
SearchMovies(v,WantMovie);
return 0;
}
void LoadMovies (vector<video> &v)
{
video L;
ifstream myfile;
myfile.open ("Movies.txt");
if (myfile.is_open())
{
cout <<"Loading Movie Catalog..."<<endl<<endl;
int i=0;
while (!myfile.eof())
{
myfile.ignore();
//cout<<i<<endl<<endl;
v.push_back(L);
getline(myfile,v[i].Title);
getline(myfile,v[i].Category);
myfile>>v[i].YOP;
myfile>>v[i].Gross;
i++;
}
myfile.close();
}
else cout<<"Unable to open file."<<endl;
}
void SearchMovies (vector <video> &v, string s)
{
s[0] = toupper(s[0]);
unsigned int i;
for (i=0; i<v.size(); i++)
{
if (v[i].Title.compare(0,s.size(),s)==0)
{
break;
}
}
if (i >=v.size()){
i =0;
}
switch(i)
{
case 1:cout<<"You have chosen "<<s<<endl;
break;
default:
cout<<"That movie is not currently in the library, please choose a different one."<<endl;
break;
}
}
First character ignored in flat file.
=========Data File==========
Captain America: The First Avenger
Action, Adventure, Sci-Fi
2011
1786.65
Iron Man
Action, Adventure, Sci-Fi
2008
585.2
The Incredible Hulk
Action, Adventure, Sci-Fi
2008
134.52
Iron Man 2
Action, Adventure, Sci-Fi
2010
312.43
Thor
Action, Adventure, Fantasy
2011
181.03
The Avengers
Action, Adventure, Sci-Fi
2012
623.28
Iron Man 3
Action, Adventure, Sci-Fi
2013
409.01
If you are still stuck, then think about what you have to accomplish to read each struct from your datafile. You need to read 4 pieces of information, 2-strings, 1-int, 1-float. You are thinking correctly to read into a temporary struct L, but what you want to do is validate you have correctly filled all four members of L before adding L to your vector.
To accomplish a successful read of all four members, you must first read the first-member. Condition your read-loop on reading the first member, e.g.:
while (getline (myfile, L.Title)) { /* validate each read */
After having read the first member, validate each of the next three members being read:
if (getline (myfile, L.Category) &&
myfile >> L.YOP && myfile >> L.Gross) {
v.push_back(L); /* before adding temp struct to vector */
i++;
}
It's risky to presume you only have a single '\n' following Gross in your input file and makes your read fragile. Ignore all characters to end of line, e.g.
/* ignore all characters to \n */
myfile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Do not use Magic Numbers or Hardcode Filenames in your code. main() takes arguments which allows you to pass a filename to read data from on the command line (or you can take it as input), but don't bury a hardcoded filename down in a funciton:
myfile.open ("Movies.txt");
Instead, pass the filename on the command line, e.g.
int main (int argc, char **argv)
{
if (argc < 2) { /* validate at least 1 argument given for filename */
std::cerr << "usage: " << argv[0] << " filename\n";
return 1;
}
...
LoadMovies(v, argv[1]); /* pass filename to open to LoadMovies */
There is no reason for six calls to std::cout to display each movie. One will do:
for (unsigned int i=0; i<v.size(); ++i)
std::cout << "----------------------------------------------------\n"
<< v[i].Title << '\n'
<< "Category: " << v[i].Category << '\n'
<< "Year of Publication: " << v[i].YOP << '\n'
<< "Gross: " << v[i].Gross << '\n'
<< "----------------------------------------------------\n\n";
Have SearchMovies() provide a meaningful return to indicate success/failure back in the caller. You ideally want to separate your implementation (your logic/calculations) from your interface (your output to the user). If you provide a meaningful return type for SearchMovies() you can remove the output from the function completely and have that provided back in main() based upon your return. A simple return of type int is fine. -1 to indicate not found in library, or return the index in your vector on success. Example:
int SearchMovies (std::vector<video>& v, const std::string& s)
{
int i;
std::string slower (s); /* string s to lowercase */
std::transform (slower.begin(), slower.end(),
slower.begin(),
[](unsigned char c){ return std::tolower(c); });
for (i = 0; i < (int)v.size(); i++) { /* movie title to lowercase */
std::string mlower (v[i].Title);
std::transform (mlower.begin(), mlower.end(),
mlower.begin(),
[](unsigned char c){ return std::tolower(c); });
if (mlower == slower) /* compare lowercase movie and string */
return i;
}
return -1; /* if not found, return -1 (cannot be an index) */
}
(note: you can loop converting each character to lowercase, or you can just use std::transform to convert all characters.)
By converting both the movie title as well as your WantMovie search string, you can make an comparison in your search function.
Then in main() you can simply output the movie title at that index within your vector v, e.g.
std::cout << "Please type in what movie you want.\n";
if (getline (std::cin, WantMovie)) {
int index = SearchMovies (v, WantMovie);
if (index != -1) /* validate search and output here */
std::cout << "You have chosen: " << v.at(index).Title << '\n';
else
std::cout << WantMovie << " is not currently in the library.\n";
}
Your search will then work regardless of the case the user uses to type in the search term, e.g.
...
Please type in what movie you want.
iron man 3
You have chosen: Iron Man 3
or
...
Please type in what movie you want.
thor
You have chosen: Thor
That should get you pointed in the right direction. If you run into additional stumbling blocks, let me know and I'm happy to help further.
Hello I do a program and in my program I have a class Customer.
In order to save the customer on the computer I create a file and separate every data of customer with :: like name::password::phonenbr. But my problem is if I write the line that is in the comment on my code the data will be save into the file, but If I write the same line in the if() that checks if t the file is empty this doesn't do anything although that I see with the compiler that there is no problem with this line.
If you can help me it will be graceful !
void Shop::Add_Customer()
{
fstream myfile; myfile.open("CustomerFile.txt");
string name, password, phonenbr;
string buffer, delimitor = "::";
system("cls");
cout << "Name of the customer: "; cin >> name;
cout << "Password of the customer: "; cin >> password;
cout << "Phone number of the customer: "; cin >> phonenbr;
if (!myfile.is_open())
{
myfile.open("CustomerFile.txt", ios::out);
}
//myfile << name + delimitor + password + delimitor + phonenbr << endl;
if (myfile.peek() == std::ifstream::traits_type::eof())
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
}
else
{
while (getline(myfile, buffer))
{
if (CheckIfCustomerExist(buffer, name, phonenbr) == true)
{
cout << "Customer already exist" << endl;
}
else
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
cout << "Customer insert in the file " << endl;
}
}
}
}
The EOF flag in the stream is set when any read of the stream fails because it tried to read past the end of the stream. Once EOF is set the stream is in a bad state and cannot be read or written until the EOF flag is cleared.
Here is a really simple example of what is going on:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream myfile("CustomerFile.txt", ios::out);
if (!myfile.is_open())
{
cout << "file not open." << endl;
}
else
{
if (myfile.peek() == std::ifstream::traits_type::eof())
{
if (myfile.eof())
{
cout << "Need to clear the EOF flag." << endl;
}
}
}
}
Peeking at EOF set the EOF flag, putting the stream in an error condition and making it unwritable. Since we want to extend the file, we need to clear that flag with the aptly named clear method.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream myfile("CustomerFile.txt", ios::out);
if (!myfile.is_open())
{
cout << "file not open." << endl;
}
else
{
if (myfile.peek() == std::ifstream::traits_type::eof())
{
if (myfile.eof())
{
cout << "Need to clear the EOF flag." << endl;
}
myfile.clear();
if (!myfile.eof())
{
cout << "OK. EOF clear now." << endl;
}
}
}
}
Off topic stuff:
The following code
while (getline(myfile, buffer))
{
if (CheckIfCustomerExist(buffer, name, phonenbr) == true)
{
cout << "Customer already exist" << endl;
}
else
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
cout << "Customer insert in the file " << endl;
}
}
will repeat for every line in the file, presumably checking the input customer against every customer in the file one by one. Every time the input customer does not match, the input customer will be added to the file. This means the input customer is likely to be added to the file several times. Worse, the program is reading and writing the same file at the same time and will probably wind up corrupting the file.
It would be better to read and compare and then if a match is not found advance to the end of the file and add the input customer.
In addition, the file open logic is needlessly complicated and may still fail
fstream myfile; myfile.open("CustomerFile.txt");
if (!myfile.is_open())
{
myfile.open("CustomerFile.txt", ios::out);
}
The first call to open will certainly fail if the file is not present, forcing the second call to open. Might as well just add the ios::outto this call and be done with it. The second call top open may fail for other reasons and is not tested for success, so I recommend
fstream myfile("CustomerFile.txt", ios::out);
if (!myfile.is_open())
{
perror("file not open: ");
}
else
{
// your code goes here
}
Documentation for perror
The root of your problem lies in your if-statement's condition:
(myfile.peek() == std::ifstream::traits_type::eof())
Apperantly, your file is open in fstream mode in the line:
fstream myfile; myfile.open("CustomerFile.txt");
Now the only reason I can get for why your if-statement's condition is not met is because the file modes are different. I'm not sure whether I am right or not (feedback is welcome in the comments box), but that's a reason I can come up with.
I tried one of my own methods that always works, and it worked for your code also. I replaced the following lines in your code:
if (myfile.peek() == std::ifstream::traits_type::eof())
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
}
With these lines:
myfile.seekg (0, ios::end);
int length = myfile.tellg();
if (length == 0)
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
}
The first line myfile.seekg (0, ios::end); gets the distance between the 2 points specified in the brackets. 0 and ios::end are self explanatory; 0 is the start of the file and ios::end is the end of the file.
The second line int length = myfile.tellg(); stores the value seeked in the above line in an int variable called length. The length is the number of characters the "cursor" would have to move to get from start to end of this file (try to imagine the cursor as the blinking thing similar to the one in Microsoft Word that is in front of the word you are typing, except here, you cannot see the cursor in your text file moving from start to end).
This if-condition is pretty straightforward. If the length is zero, meaning that the cursor has to move 0 points to get from the start of the file to the end of the file, then write whatever you want to that file. This technique worked (at least it did for me).
On a side note, there are a couple of other areas where your code can improve. For example, why have you added this if-statement:
if (!myfile.is_open())
{
myfile.open("CustomerFile.txt", ios::out);
}
This code is a repetition of these lines of your code:
fstream myfile; myfile.open("CustomerFile.txt");
The .open() command already fulfills the if-statement I pointed out. If the file specified in the open() is found, then it will open that file; else it will continue to create that new file. Therefore, that if-statement is redundant and should be removed as it consumes unnecessary CPU power and slows your program down (not by a lot, but you will soon realize that every millisecond counts in running your code; efficiency is key). I would recommend you to remove that if-statement.
Another issue is your 3 variables that you accept for input. Given that they are strings, why do you use the cin >> method? Using cin will only take the first word in your sentence; in your following line:
cout << "Name of the customer: "; cin >> name;
If you enter John Doe, it will only save John to the name variable, and it will move "Doe" to the next input variable, which is password in your case. If there is no other cin, then it will ignore the words after the space. Therefore, use the following line for all your input points:
getline(cin, name);
This function will get all the words and spaces as a single sentence till the point you hit Enter, unlike cin that will only get the first word and ignore the rest of the sentence.
Finally, your phone number should be of type int. I'll leave that for you to fix as per your requirement.
Hope I answered your question and hoped my tips were helpful. Good luck!
EDIT: Another point I missed about your code was that your while loop runs for every line. This means it will check for the particular customer's name at every single line of the file. This is not what you want. You want to read every line in the file, BUT if you find the customer, then you want to terminate the function without continuing for the next line. Also, you only want to print an error statement AFTER you have read the entire file, not just a single line.
else
{
int check = 0;
while (getline(myfile, buffer))
{
if (CheckIfCustomerExist(buffer, name, phonenbr) == true)
{
cout << "Customer already exist" << endl;
check = 1;
break;
}
}
if (check == 0)
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
cout << "Customer insert in the file " << endl;
}
}
What this piece of code does is that it runs through every line, checking for the customer in that line. If that line has the customer record, then it sets an check value of type int to 1 from 0 and the break statement terminates the while loop. After reading the entire file, it moves on to an if-statement. In this statement, if the check variable still has 0, it means that the file did not have the customer, in which the new record will be added to the file.
Also, I said that the phone_number should be an int value. I take that back as upon further though and input from fellow StackOverflow users, the phone number is better suited as a string value as its format may not be stored properly as an int value (for example, 0059875 will be stored as 59875).
I work on using extensible hash to find the query FASTER.
my code is this steps:
1)read the main text file ( hudge file 4 GiB)
the file is some thing like this :
12435 alex romero
13452 jack robert
13485 marya car
45132 gun tribble
...
the user want to know that for example the key 12435 is related to what ?(answer:alex romero)
2)create a hash table for the keys in the file (i means 12435,13452,13485,...)
and i save this tables dynamically in hard disk in some text files named:0.txt,1.txt,2.txt and ....
3)when the user get query to the program then the program must calculate the hash function on its value and find the file that must be read then it is faster to find the result.
i have a function:
#define LIMIT 7
void writeInFile(int key , const char* charPos ){
int remainder = key%(LIMIT*LIMIT);
string myFileName;
ostringstream convert;
convert << remainder ;
myFileName = convert.str();
myFileName += ".txt";
FILE *my_file;
my_file = fopen(myFileName.c_str() ,"a");
fputs("\n" ,my_file);
fputs(charPos , my_file);
//fclose(my_file);
}
i wondered that when i use fclose then the speed of the program will reduced !!!
then i dont use it at the end of the function but a problem that is when i use this function many times i can't close them then i cant get access to the files.
i want to create a "list" of FILEs that i can send refrence of them to the function like: FILE &* myFiles[] or FILE &** myFiles as 3th parameter that function gets...
but i see the errors .i dont know how is its syntax of this.i means some syntax like:
void writeInFile(int key , const char* charPos , FILE &*myFiles[] ) // this makes error
the other method that i think is that can i close those files that now I can't access to them ? or can i change my code that cause this ?
update:this is my full code
#include <iostream>
#include <fstream>
#include <limits>
#include <string>
#include <sstream>
#include <stdio.h>
#include <vector>
#define LIMIT 7
using namespace std;
void writeInFile(int key , const char* charPos ){
int remainder = key%(LIMIT*LIMIT);
string myFileName;
ostringstream convert;
convert << remainder ;
myFileName = convert.str();
myFileName += ".txt";
FILE *my_file;
my_file = fopen(myFileName.c_str() ,"a");
fputs("\n" ,my_file);
fputs(charPos ,my_file);
//fclose(my_file);
}
int main(){
string fileName;
cout << "hello, please inter your file destination : " ;
cin >> fileName;
ifstream myFile ;
myFile.open(fileName.c_str() ,ifstream::in |ifstream::binary);
cout << "building the hash,please wait";
string havij;//:D this is an unusable variable in this section :))
int current;
int index;
int isCout=0;
char buffer [10];
//FILE *my_file[49];
while(!myFile.eof()){
cout << isCout << endl;
isCout++;
index = myFile.tellg();
itoa(index , buffer ,10);
//cout << buffer << endl;
myFile >> current;
writeInFile(current ,buffer);
getline(myFile,havij);
}
myFile.close();
fstream test;
//for(int i =0 ; i<LIMIT*LIMIT-1 ; i++){
// fclose(my_file[i]);
//}
cout << endl << "static extensible hash structure builded please inter your query : " ;
int query;
cin >> query;
int remainder = query%(LIMIT*LIMIT);
string myFileName;
ostringstream convert;
convert << remainder ;
myFileName = convert.str();
myFileName += ".txt";
ifstream myFile2;
//myFile2 is now the files that create by program like : 12.txt ,25.txt ,....
myFile2.open(myFileName.c_str() , ifstream::in | ifstream::binary);
ifstream mainFile;
mainFile.open(fileName.c_str(), ifstream::in | ifstream::binary);
int position;
string wanted;
int tester;
while(!myFile2.eof()){
myFile2 >> position;
mainFile.seekg(position ,ios::beg);
mainFile >> tester;
if (tester == query ){
getline(mainFile ,wanted);
cout << "the result of the key " << tester << " is " << wanted << endl;
}
}
return 0;
}
Or you could do this:
void writeInFile(int key , const char* charPos , std::vector<std::ofstream> & myFiles );
I find this makes my brain hurt less.
If you don't close your file in the same context where the FILE* variable is declared, you are leaking that file descriptor. At some point you are going to run out of resources and the program will crash.
Since you are using C++ from the snippet you've shown, then you would be much better off using std::vector and std::ofstream.
void writeInFile(int key, const char* charPos, std::vector<std::ofstream> my_files )
As has been said, you should close the file in the scope it is opened. This is the default behavior for C++ streams.
However it does not mean that you should open/close for each word you add! The files you write to should be kept open as long as you have things to add to them (beware there is a limit in the number of file descriptors an OS can handle).
Therefore, you should have:
Open all destination files (*)
For each line, select the appropriate file in a table/map and write into it
Close all destination files
(*) As said, you might run into a hard limit, in this case there is not much you can do, caching is unlikely to be effective if your hash function is worth anything. A possibility would be to make several runs over the big file and saving only a portion of the hashes at each run (say run 1: hashes in [0-9], run 2: hashes in [10-19], ...).
The fundamental type FILE* or ofstream that you use is of little importance, both have comparable speed (correctly tuned).