I have such piece of code:
typedef struct reader
{
char name[50];
char card_num[50];
char title[100];
}reader_t;
int main()
{
vector<reader> vec;
ifstream input_file("D:\\lab.txt", ios::binary);
reader_t master[1];
input_file.read((char*)&master, sizeof(master));
for (size_t idx = 0; idx < 1; idx++)
{
reader temp;
strcpy(temp.name, master[idx].name);
strcpy(temp.card_num, master[idx].card_num);
strcpy(temp.title, master[idx].title);
vec.push_back(temp);
cout << "Name: " << master[idx].name << endl;
cout << "Card num: " << master[idx].card_num << endl;
cout << "Title: " << master[idx].title<<endl;
}
cout << vec.size();
getchar();
}
What is does: it reads structures from binary file into an array of structures,copies them into vector and displays structure.And yes, I do need to do like this - I need to store structures from file in vector and this is the only working way to do it I could find(if you can tell, how to read structures to vector directly from file - you are welcome).
So,everything works fine, but the problem is that I need to create a function which would be able to do the same, but with dynamic array.I wrote something like this:
void read_structs(int vec_size)
{
ifstream input_file("D:\\lab.txt", ios::binary);
//Here I commented 2 ways how I tried to create a dynamic array of structs
//reader* master = new reader[vec_size];
//reader* master = (reader*)malloc(sizeof(reader) * vec_size);
input_file.read((char*)&master, sizeof(master));
for (size_t idx = 0; idx < vec_size; idx++)
{
reader temp;
strcpy(temp.name, master[idx].name);
strcpy(temp.card_num, master[idx].card_num);
strcpy(temp.title, master[idx].title);
vec.push_back(temp);
cout << "Name: " << master[idx].name << endl;
cout << "Card num: " << master[idx].card_num << endl;
cout << "Title: " << master[idx].title<<endl;
}
}
And that worked fine too unless I tried to run it.VS wasn't higlighting error in my code, it just was throwing an exception right as the moment when the program tried to access master[0].name.
There is absolutely no point in the temp struct. See, the
vec.push_back(temp);
is already using copy constructor, so copy constructor must work and then the set of strcpy is not doing anything different from that, so just go with
vec.push_back(master[0]).
You can't read into vector directly. You do need to read into temporary. So that is correct. Except I suppose you want to read all entries from the file no matter how many of them there are, so you need to put the read itself also into the loop.
There is not much point in creating an array of one element.
reader_t master[1];
input_file.read((char*)master, sizeof(master));
// ^ you *don't* need & here, arrays degrade to pointers automatically
and
reader_t master;
input_file.read((char *)&master, sizeof(master));
// ^ but you do need & here.
are equivalent. I would go with the later.
So we are basically down to:
reader temp; // calling it temp; the master name makes no sense.
while (input_file.read((char*)&temp, sizeof(temp)))
// read returns input_file and input_file is false if last operation failed
{
vec.push_back(temp);
// verify the stored values by reading back vfrom vec.back().
cout << "Name: " << vec.back().name << endl;
cout << "Card num: " << vec.back().card_num << endl;
cout << "Title: " << vec.back().title<<endl;
}
In the second example, you didn't initialize master, so it obviously crashed.
There is a more C++ approach though. First, you define a read operator for the structure:
std::istream &operator>>(std::istream &in, reader &r) {
return in.read((char *)&r, sizeof(r));
}
and then you simply read the vector using the istream_iterator:
vec.assign(std::istream_iterator<reader>(input_file),
std::istream_iterator<reader>());
and the standard library will generate the above loop for you.
Related
I'm trying to build a program that can register a user to the database (still learning cpp, I hope that in the near future I'll be able to work with database).
What I'm trying to do with this code is to check whether an index of array is empty for the user to store an ID in it. If it isn't empty, I want the program to keep looking for an empty index of array, for the new info to be stored in.
Here is the code:
void registro() {
std::string userid[3];
userid[0] = "Houkros"; // eventually I'll try to have this being read from a file or server database..
std::string userpass[3];
std::string usermail[3];
std::string userkey[3];
std::string getUid[3];
std::string getUpass[3];
std::string getUmail[3];
std::string getUkey[3];
std::cout << std::endl << " >>>> REGISTRATION <<<< " << std::endl;
std::cout << " =============================================== " << std::endl;
std::cout << std::endl;
std::cout << "Please, enter the desired user id: " << std::flush;
if (userid[0].empty())
{
std::cin >> userid[0];
}
else {
std::cin >> userid[1];
}
for (int i = 0; i < 2; i++)
{
std::cout << " Element of array: " << i << " is > " << userid[i] << std::endl;
}
Please consider the following definitions for an "empty" array element:
a) not initialised (unhelpful, cannot be checked)
b) never yet written to (same as a) )
c) contains "" (possible, but means that "" must not be accepted as an actual content)
d) is empty according to a second array in which that info is maintained (this is what I almost recommend)
e) contains a struct with a string and a maintained "empty" flag (this I recommend)
Whatever you do, make sure that you init all variables and array elements before first read-accessing them; i.e. in all cases first write something meaningful to it.
I have a bit of an issue with my program. I have a function void loadData() which will load the data from a text file customers.txt and store each line of data into a Linked List. My concern is, specifically with how I/O works. I managed to get the data from the text file into and stored into a linked list data member variable. When i call that variable i get the answer i want printed onto the console.
std::cout << "Group Name: " << tempCustomer->groupName << std::endl;
However, i decided to run a console output command later in the function to test if all the variables had the right data, i realize that it was all over the place. I'm not sure why its not working.
Here is the loadData() function
void Groups::loadData(){
fin.open("customers.txt");
char holder[MAX_SIZE];
if(!fin.is_open())
std::cerr << "Could not access file" << std::endl;
else{
while(!fin.eof()){
Customers *tempCustomer = new Customers;
fin.getline(holder,MAX_SIZE,';');
tempCustomer->groupName = holder;
std::cout << "Group Name: " << tempCustomer->groupName << std::endl;
fin.getline(holder,MAX_SIZE,';');
tempCustomer->name = holder;
fin.getline(holder,MAX_SIZE,';');
tempCustomer->email = holder;
fin >> tempCustomer->choice;
fin.get(); //gets the last character, which is '\n'
fin.ignore(); //ignores the next character which is the '\n'
tempCustomer->next = NULL;
std::cout << "What does the temp Node Store?" << std::endl;
std::cout << "Group Name: " << tempCustomer->groupName << std::endl;
std::cout << "Name: " << tempCustomer->name << std::endl;
std::cout << "Email: " << tempCustomer->email << std::endl;
std::cout << "Choice: " << tempCustomer->choice << std::endl;
//addCustomerToLL(tempCustomer);
tempCustomer = NULL;
delete tempCustomer;
}
}
fin.close();
}
Here is the Console out put:
Group Name: Jonathan Group
What does the temp Node Store?
Group Name: vazquez.jonathan#pcc.edu
Name: vazquez.jonathan#pcc.edu
Email: vazquez.jonathan#pcc.edu
Choice: 2
Here is the text file customers.txt
Jonathan Group;Jonathan;vazquez.jonathan#pcc.edu;2
This is a school assignment, i'm to store all the customers from the text file into a linked list. I'm also to use c strings as strings rather than c++ version of strings. Let me know if the other files are necessary, i didnt include them since well nothing in this function utilize anything else outside the func besides the ifstream fin; private variable i have in the class and the const int MAX_SIZE = 256; global variable.
Assuming you're not allowed to use std::string, you need to allocate memory for each string.
So replace this:
fin.getline(holder,MAX_SIZE,';');
tempCustomer->groupName = holder;
with:
fin.getline(holder, MAX_SIZE, ';');
char *s = new char[strlen(holder) + 1];
strcpy(s, holder);
tempCustomer->groupName = s;
You should release the memory you allocate when you no longer need it, so create a destructor for your Customers class:
Customers::~Customers()
{
delete[] groupName;
}
It's because the holder changes when you read a new line,but your all string in your Customer points to the same holder which stores the last line you read.
Change the type of name,email etc to char[MAX_SIZE] may help.
I'm working on a project that involves binary files.
So I started researching about binary files but I'm still confused about how to write and fill a vector from that binary file that I wrote before
Here's code: for writing.
void binario(){
ofstream fout("./Binario/Data.AFe", ios::out | ios::binary);
vector<int> enteros;
enteros.push_back(1);
enteros.push_back(2);
enteros.push_back(3);
enteros.push_back(4);
enteros.push_back(5);
//fout.open()
//if (fout.is_open()) {
std::cout << "Entre al if" << '\n';
//while (!fout.eof()) {
std::cout << "Entre al while" << '\n';
std::cout << "Enteros size: "<< enteros.size() << '\n';
int size1 = enteros.size();
for (int i = 0; i < enteros.size(); i++) {
std::cout << "for " << i << '\n';
fout.write((char*)&size1, 4);
fout.write((char*)&enteros[i], size1 * sizeof(enteros));
//cout<< fout.get(entero[i])<<endl;
}
//fout.close();
//}
fout.close();
cout<<"copiado con exito"<<endl;
//}
}
Here's code for reading:
oid leerBinario(){
vector<int> list2;
ifstream is("./Binario/Data.AFe", ios::binary);
int size2;
is.read((char*)&size2, 4);
list2.resize(size2);
is.read((char*)&list2[0], size2 * sizeof(list2));
std::cout << "Size del vector: " << list2.size() <<endl;
for (int i = 0; i < list2.size(); i++) {
std::cout << i << ". " << list2[i] << '\n';
}
std::cout << "Antes de cerrar" << '\n';
is.close();
}
I don't know if I'm writing correctly to the file, this is just a test so I don't mess up my main file, instead of writing numbers I need to save Objects that are stored in a vector and load them everytime the user runs the program.
Nope, you're a bit confused. You're writing the size in every iteration, and then you're doing something completely undefined when you try to write the value. You can actually do this without the loop, when you are using a vector.
fout.write(&size1, sizeof(size1));
fout.write(enteros.data(), size1 * sizeof(int));
And reading in:
is.read(&list2[0], size2 * sizeof(int));
To be more portable you might want to use data types that won't change (for example when you switch from 32-bit compilation to 64-bit). In that case, use stuff from <cctype> -- e.g. int32_t for both the size and value data.
Let's say I have a program that does the follow:
for (i=1; i<10; i++)
{
computeB(i);
}
where the computeB just outputs a list of values
computeB(int i)
{
char[6] out_fname="output";
//lines that compute `var` using say, Monte Carlo
string fname = out_fname + (string)".values";
ofstream fout(fname.c_str());
PrintValue(fout,"Total Values", var);
}
From another file:
template <class T>
void PrintValue(ofstream & fout, string s, T v) {
fout << s;
for(int i=0; i<48-s.size(); i++) {
fout << '.';
}
fout << " " << v << endl;
}
Before implementing that loop, computeB just outputted one file of values. I now want it to create multiple values. So if it originally created a file called "output.values", how can I write a loop so that it creates "output1.values", "output2.values", ..., "output9.values"?
EDIT: I forgot to mention that the original code used the PrintValue function to output the values. I originally tried to save space and exclude this, but I just caused confusion
Disregarding all the syntax errors in your code ...
Use the input value i to compute the output file name.
Use the file name to construct an ofstream.
Use the ofstream to write var to.
Here's what the function will look like:
void combuteB(int i)
{
char filename[100];
sprintf(filename, "output%d.values", i);
ofstream fout(filename);
fout << "total values";
fout << " " << var << endl; // Not sure where you get
// var from. But then, your
// posted code is not
// exactly clean.
}
You can use std::to_string() to convert from an int to a string:
void computeB(int i)
{
if (std::ofstream fout("output" + std::to_string(i) + ".values"))
fout << "total values" << " " << var << '\n';
else
throw std::runtime_error("unable to create output file");
}
Okay, so I'm trying to read input from a binary file. I've changed this code a bit, but with this version, I'm getting an access violation error... So it's trying to access something that isn't there. Here's my source code for the problem area:
void HashFile::fileDump (ostream &log)
{
HashNode *temp = new HashNode;
fstream bin_file;
bin_file.open ("storage_file.bin", ios::in | ios::binary);
for(int i = 0; i < table_size; i++)
{
bin_file.seekg( i * sizeof(HashNode) );
bin_file.read( (char *)&temp, sizeof(HashNode) );
printDump(HashNode(temp->title, temp->artist, temp->type, temp->year,
temp->price), log, i);
}
bin_file.close();
}
void HashFile::printDump(HashNode A, ostream &log, int N)
{
log << "(" << N << ") " << A.title << ", " << A.artist
<< ", " << A.type << ", " << A.year << ", $"
<< setprecision(2) << A.price << endl;
}
I know that I should have some kind of error checking. Right now the error is occurring in the printDump function. Whenever I try to output to the log I get an access violation error. However, I change the log to cout and my code will run fine somewhat. It will read the binary file I've created correctly until it gets to the last element. For what I've been testing with, table_size should be equal to 5. So I get into the for loop and i is incremented until it reaches 5 and then it keeps going. table_size is being changed to some random value even though I haven't physically touched it. Am I somehow writing over table_size's address in memory?
Here is the definition of my Node:
class HashNode
{
public:
HashNode();
~HashNode();
HashNode(string title, string artist, string type, int year, float price);
friend class HashFile;
private:
char title [35];
char artist [25];
char type [12];
int year;
float price;
};
This
bin_file.read( (char *)&temp, sizeof(HashNode) );
should be this
bin_file.read( (char *)temp, sizeof(HashNode) );
You are getting confused over pointers.
Whether that code will actually work depends strongly on the definition of Node which you haven't shown.
Also the code leaks memory as temp is never deleted. It would be better not to allocate temp at all, like this
void HashFile::fileDump (ostream &log)
{
HashNode temp;
fstream bin_file("storage_file.bin", ios::in | ios::binary);
for(int i = 0; i < table_size; i++)
{
bin_file.seekg( i * sizeof(HashNode) );
bin_file.read( (char *)&temp, sizeof(HashNode) );
printDump(HashNode(temp.title, temp.artist, temp.type, temp.year, temp.price), log, i);
}
}
Not clear why you feel the need to create a new node from temp, why not just pass temp to printDump? Like this
printDump(temp, log, i);
But without seeing the definition of Node I can't say for sure.
Also no need to close the file, that happens automatically, also opening the file in the constructor is a little cleaner IMHO.
EDIT
OK having seen the definition of Node this would be my recommendation
void HashFile::fileDump(ostream &log)
{
fstream bin_file("storage_file.bin", ios::in | ios::binary);
for(int i = 0; i < table_size; i++)
{
bin_file.seekg(i * sizeof(HashNode));
HashNode temp;
bin_file.read((char *)&temp, sizeof(HashNode));
printDump(temp, log, i);
}
}
Also I would change printDump to use a const reference, this avoids copying a Node object (it is quite big).
void HashFile::printDump(const HashNode& A, ostream &log, int N)
{
log << "(" << N << ") " << A.title << ", " << A.artist
<< ", " << A.type << ", " << A.year << ", $"
<< setprecision(2) << A.price << endl;
}