I'm getting Read access violation while accessing objects from file C++. - c++

In the below class error is in the init function where i load the class object I stored in the file to the vector Items.
class Item
{
std::string item_code;
std::string item_name;
std::string unit_name;
unsigned int price_per_unit;
double discount_rate;
static std::vector<Item> Items;
friend std::ostream& operator<< (std::ostream&, Item&);
public:
static void PrintAll();
static void Init();
~Item();
};
Default constructor is the one which reads data from user and writes into file. Below is the code of default constructor.
Item::Item(int a)
{
std::cout << "Item name : ";
std::getline(std::cin, item_name);
std::cout << "Unit (Kg/g/Qty) : ";
std::getline(std::cin, unit_name);
std::cout << "Price per unit : ";
std::cin >> price_per_unit;
std::cout << "Discount Rate : ";
std::cin >> discount_rate;
std::cin.ignore();
std::cout << "Product code (has to be unique) : ";
std::getline(std::cin, item_code);
std::ofstream outfile;
outfile.open("Files\\Items.txt", std::ios::out | std::ios::app);
outfile.write((char*)&(*this), sizeof(Item));
outfile.close();
}
Below is the Init() function for which read access violation is thrown at.
void Item::Init()
{
std::ifstream infile("Files\\Items.txt", std::ios::in);
if (!infile.is_open())
{
std::cout << "Cannot Open File \n";
infile.close();
return;
}
else
{
Item temp;
while (!infile.eof())
{
infile.read((char*)&temp, sizeof(temp));
Item::Items.push_back(temp);
}
}
infile.close();
}
Even though i am checking for eof, read access violation is thrown. Please give me some advice on this issue.

infile.read((char*)&temp, sizeof(temp));
This fills the temp object with junk from the file. It's supposed to contain valid std::string objects and whatever is in the file, it can't possibly be a valid std::string object. If you don't see why, consider that creating a valid std::string object requires allocating memory to hold the string data -- that's what the std::string constructor does. Reading data from a file can't possibly do this.
A file is a stream of bytes. To write data to a file, you need to define some way to represent that data as a stream of bytes. You need to encode its length if it is variable length. To read it back in, you need to handle the variable length case as well. You need to convert the file data to an appropriate internal representation, such as std::string. This is called "serialization".

std::string size is variable, you can try the following definition
char item_code[20];
char item_name[20];
char unit_name[20];

Related

I'm trying to write a char into a .txt file by using the ifstream getline function. But i get an Error Message

E0304 no instance of overloaded function "std::basic_ifstream<_Elem, _Traits>::getline [with _Elem=char, _Traits=std::char_traits]" matches the argument list
Im using a struct for the Information:
struct customer {
int id;
char name;
char phone;
char address;
};
And im trying to write the Customers Information into a .txt file:
void customerData()
{
ifstream ifs;
ifs.open("Customer.txt");
int custNum = 0;
while (!ifs.eof())
{
ifs >> cust[custNum].id;
ifs.ignore();
ifs.getline(cust[custNum].name, 100, ';');
ifs.getline(cust[custNum].phone, 15, ';');
ifs.getline(cust[custNum].id, 15, ';');
ifs.getline(cust[custNum].address, 1500);
custNum++;
}
}
I cant figure out how to fix the above posted Error on the getline functions.
There are big mistakes in your code that guys pointed out.
You are not writing to the file, you are reading it.
You cannot store a full name in a single character.
Actually, if you want to store this data, you should use character array or std::string.
So your struct will be like this :
struct customer {
int ID;
char name[100];
char phone_number[15];
char address[1500];
/*
OR
int ID;
std::string name;
std::string phone_number;
std::string address;
in this case it's better to use std::string instead of using 1500 characters for address
*/
}
Also, getline is not for writing to the file (as you said you want to write in file) , it is used for reading from the file.
So your customerData function will look like this:
// saving in file
ofstream ofs(Customer.txt);
// check if file is created
if(ofs.is_open(){
ofs << name << '\n';
ofs << address << '\n';
ofs << phone_number << '\n';
ofs << id << '\n';
// This is a simple way to store data in a file.
// There are other ways to store data in a file..
// I used this because you can use getline to read them and get the data as lines.
}

Using a function to open a file and then having other functions utilize that file afterwards?

I have a computer science assignment which requires me to have a separate function just to open the file, and then another function which will then process the data in that file and then some others to do some operations with that data. Anyways, I'm having trouble in how to be able to let other functions use that opened file. References with '&' or'*' are confusing me and I'm unsure if I have to use one or not, of course, though I'm pretty sure I'll have to pass at least something to the next function. The main intent when dealing with the file is to open it(openFile) and then have another function(getData) to sort the data into two different arrays. One for the names, and one for the amounts next to them. The file would be written as:
Johnson 6000
Brown 5000
Miller 4000
Duffy 2500
Robson 1800
My code is as follows:
'''
#include <iostream>
#include <string>
#include <iomanip>
#include <fstream>
using namespace std;
void openFile();
void getData();
void computePercentages();
void sortVotes();
void display();
void displayWinner();
int main() {
openFile();
getData();
return 0;
}
void openFile(){
string fileName;
cout << "Enter the name of the file to open: ";
cin >> fileName;
ifstream file;
file.open(fileName.c_str());
}
void getData(){
int count = 0;
while(!file.eof()){
string names[count];
int votes[count];
cin >> names[count];
cin >> votes[count];
count ++;
}
}
'''
One way is to have openFile return the file stream object, then pass it to getData.
ifstream openFile()
{
string fileName;
cout << "Enter the name of the file to open: ";
cin >> fileName;
ifstream file(fileName);
return file;
}
void getData(ifstream &file)
{
int count = 0;
while(file){
string names[count];
int votes[count];
cin >> names[count];
cin >> votes[count];
count ++;
}
}
int main()
{
ifstream file = openFile();
if (file)
{
getData(file);
}
}
Note that this answer does not fix other issues in your code. For example, in getData you're using variable-length arrays which are non-standard and won't work on all compilers, and those arrays are constructed and destroyed each time through the while loop.
There are many ways to do it..
Here is a simple way.. using global variables.
I made ifstream file; as global..
This is not good way.. but simple..
#include <iostream>
#include <string>
#include <iomanip>
#include <fstream>
using namespace std;
void openFile();
void getData();
void computePercentages();
void sortVotes();
void display();
void displayWinner();
ifstream file;
int main() {
openFile();
getData();
return 0;
}
void openFile(){
string fileName;
cout << "Enter the name of the file to open: ";
cin >> fileName;
file.open(fileName.c_str());
}
void getData(){
int count = 0;
while(!file.eof()){
string names[count];
int votes[count];
cin >> names[count];
cin >> votes[count];
count ++;
}
}
Your getData() function has some problems:
void getData(){
int count = 0;
while(!file.eof()){ // this is almost never the correct check
string names[count]; // you declare new VLA:s (non-standard) every iteration
int votes[count]; // -"-
cin >> names[count]; // and you put a value in it out of bounds.
cin >> votes[count]; // -"-
count ++;
} // both arrays are destroyed here
}
file.eof() does not return true until you've tried to read beyond the end of the file. If you've read the last value, it will not be set. Only when you try next time will it be set.
The arrays you declare inside the while loop will be destroyed at the end of the loop. After the loop is finished, you have no arrays.
When you declare an array of count elements, you can access those elements using 0 to count-1 inclusive. You access element count which is out of bounds so your program has undefined behaviour.
VLA:s (variable length arrays) does not exist in standard C++ (but does as an extension in some compilers). If you know exactly how many elements you need to store, you can use std::array instead, but in this case, use a std::vector.
It uses a global file variable (that doesn't even exist). Try to stay away from global variables if you can.
The records in your data file should be kept together instead of putting each column in a separate array. A simple placeholder for each record in your file could look like this:
struct record {
std::string name{};
int vote{};
};
With that, you only need one array (or std::vector).
std::vector<record> records;
It'd also be good if one could extract one complete record from a stream using the same >> operator as you used for int and std::string. Like this:
record temp; // declare a variable using your own type, "record"
while(file >> temp) { // read records until no more can be read
records.push_back(temp) // add one record to records
}
A function to read one record from an istream, like an ifstream:
std::istream& operator>>(std::istream& is, record& r) {
// You may want to use getline here instead in case the names contain spaces.
return is >> r.name >> r.vote; // extract name and vote from is and return is
}
The function takes both parameters (is and r) by reference. That means that whatever is done to the parameters inside the function affects the variables that were used to call the function. file >> temp results in a call to the above function where is is a reference to file and r is a reference to temp.
For openFile() I'd suggest:
std::ifstream openFile(const std::string& fileName) { // return the ifstream by value
return std::ifstream{fileName};
}
Getting the filename from the user doesn't have anything to do with opening the file, so get the filename before calling the function. The above function lets you call openFile() and get an ifstream in return:
std::ifstream file = openFile(fileName);
You can now call getData() using file, but it needs to be able to receive it. Standard stream objects can't be copied (passed by value), but we don't need to. Just make getData() receive a reference to the stream. I'd make it an istream instead of an ifstream to be able to read from any istream decendant:
std::vector<record> getData(std::istream& is) {
// create a vector, read data from "is" and put it in vector and return vector when done
}
When all is pieced together, you could have a main() looking something like this:
int main() {
std::vector<record> records;
std::cout << "Enter the name of the file to open: ";
// use getline since a filename may contain spaces
if(std::string fileName; std::getline(std::cin, fileName)) {
// if "file" is in a good state after openFile(), call getData()
if(std::ifstream file = openFile(fileName)) {
records = getData(file);
} // "file" is automatically closed when it goes out of scope
}
// print what you collected
for(const record& r : records) {
std::cout << r.name << "\t" << r.vote << "\n";
}
}
The above uses If Statements with Initializer which is a C++17 feature to help create a narrow scope for variables.

Analogous network protocol for transmitting messages using istream/ostream

This was a question from a coding challenge and I couldn't figure out the way to do it -
Implement encode() and decode() for a simple wire protocol per the prototypes below:
void encode ( const std::string& inputFilename, std::ostream& out );
void decode ( std::istream& in, const std:string& outputFilename );
Lets say there are several files and each contains a single message. As it already says from the prototype, encode must read single specified file from disk and place message in the ostream.
Decode must read single encoded message from istream and place it in the specified file. The contents of the two corresponding files must be identical.
It is easy if I could read the file in the encode method and place the data in a buffer and send them to another file in the decode method, but that is not the question. How do I read data from std::istream without someone actually typing the data on the console (like std::cin)?
Thank you in advance for your time and would love to see responses for this!
istream is a parent class for ifstream (input file stream) and istringstream (input string stream), so you can pass as a input parameter ifstream or istringstream. Do not need to use cin.
The same applies to out parameter, you can provide object of the ofstream or ostringstream. This is well know mechanism in C++ to abstract what particular type of stream you deal with.
I wrote a piece of code that works with the prototypes given in the question. I am sure there could be more ways of doing it but I thought I would share this.
class fileIO
{
private:
std::string inBuf;
public:
std::stringstream testbuf;
void encode(const std::string& inputFilename, std::ostream& out);
void decode ( std::istream& in, const std::string& outputFilename);
};
void fileIO::encode(const std::string& inputFilename, std::ostream& out)
{
//read from the input file and stream it to ostream
ifstream infile(inputFilename);
while(infile.good()){
getline(infile, inBuf);
out << inBuf;
}
cout << "Value stored in outBuf: " << inBuf << endl;
infile.close();
}
void fileIO::decode(std::istream& in, const std::string& outputFilename)
{
//read from istream and output it to a file
string val;
ofstream ofile(outputFilename);
in >> val;
cout<< "Read the istream contents: " << val << endl;
ofile << val;
ofile.close();
}
int main( int argc, char ** argv )
{
fileIO File;
string inputFile;
cout << "Enter input File Name: "<< endl;
cin >> inputFile;
File.encode(inputFile, File.testbuf);
File.decode(File.testbuf, inputFile);
return 0;
}
I am passing "a stringstream" to both encode and decode since istream and ostream are the parent class of stringstream. In this solution, the value from the input file is stored in the stringstream and gets passed on to decode where the value from the same stringstream gets written to output file.

Read in from file into structure

I want to read in from txt file into structure using fstream.
I save the data to the file in the way shown below:
To read the data i tried some cheeky stuff with getlines or tabsin<
struct tab{
int type,use;
string name, brand;
};
tab tabs[500];
ofstream tabsout;
tabsout.open("tab.txt", ios::out);
for (int i = 0; i < 500; i++){
if (tabs[i].use==1){
tabsout << tabs[i].type << " " << tabs[i].name << " " << tabs[i].brand << "\n";
}
}
tabsout.close();
//input part that fails me :(
int i=0;
ifstream tabsin;
tabsin.open("tab.txt", ios::in);
if (tabsin.is_open()){
while(tabsin.eof() == false)
{
tabsin >> tabs[i].type>>tabs[i].name>>tabs[i].brand;
i++
}
tabsin.close();
You usually want to overload operator>> and operator<< for the class/struct, and put the reading/writing code there:
struct tab{
int type,use;
string name, brand;
friend std::istream &operator>>(std::istream &is, tab &t) {
return is >> t.type >> t.name >> t.brand;
}
friend std::ostream &operator<<(std::ostream &os, tab const &t) {
return os << t.type << " " << t.name << " " << t.brand;
}
};
Then you can read in a file of objects like:
std::ifstream tabsin("tab.txt");
std::vector<tab> tabs{std::istream_iterator<tab>(tabsin),
std::istream_iterator<tab>()};
....and write out the objects like:
for (auto const &t : tabs)
tabsout << t << "\n";
Note that (like any sane C++ programmer) I've used a vector instead of an array, to (among other things) allow storing an arbitrary number of items, and automatically track how many are actually being stored.
For starters, do not use .eof() to control your loop: it doesn't work. Instead, use the stream's state after reading:
int type;
std::string name, brand;
while (in >> type >> name >> brand) {
tabs.push_back(tab(type, name, brand));
}
If your name or brand contain spaces, the above won't work and you will need to write a format where you can know when to stop abd read correspondingly, e.g., using std::getline().
You might also consider wrapping the logic to read or write an object by suitable operators.
istream& getline (istream& is, string& str, char delim);
Take a look at the third parameter, you can use std::getline to parse your line. But that is definitely not the best way to serialize objects. Instead of using a text file, you should use a byte stream.

Returning ifstream in a function

Here's probably a very noobish question for you: How (if at all possible) can I return an ifstream from a function?
Basically, I need to obtain the filename of a database from the user, and if the database with that filename does not exist, then I need to create that file for the user. I know how to do that, but only by asking the user to restart the program after creating the file. I wanted to avoid that inconvenience for the user if possible, but the function below does not compile in gcc:
ifstream getFile() {
string fileName;
cout << "Please enter in the name of the file you'd like to open: ";
cin >> fileName;
ifstream first(fileName.c_str());
if(first.fail()) {
cout << "File " << fileName << " not found.\n";
first.close();
ofstream second(fileName.c_str());
cout << "File created.\n";
second.close();
ifstream third(fileName.c_str());
return third; //compiler error here
}
else
return first;
}
EDIT: sorry, forgot to tell you where and what the compiler error was:
main.cpp:45: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’ first required here
EDIT: I changed the function to return a pointer instead as Remus suggested, and changed the line in main() to "ifstream database = *getFile()"; now I get this error again, but this time in the line in main():
main.cpp:27: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’ first required here
No, not really. ifstream doesn't have a copy constructor, and if you try to return one, that means copying the instance in your function out to wherever the return needs to go.
The usual workaround is to pass in a reference to one, and modify that reference in your function.
Edit: while that will allow your code to work, it won't fix the basic problem. Right now, you're mixing two rather different responsibilities into a single function: 1) obtain a file name, 2) open or create that file. I think if you separate those, the code will be simpler, and make it much easier to eliminate the source of the problem you're seeing.
Edit 2: Using a reference like this works perfectly well without an operator=. The general idea is something like:
int open_file(char const *name, fstream &stream) {
stream.open(name);
}
The assignment operator is neither necessary nor useful in this case -- we simply use the existing fstream via the reference. An operator= would be necessary if and only if we had to pass the argument to the ctor. With a stream, we can default construct a stream that doesn't connect to a file, and then use open to connect to the file after the fact.
bool checkFileExistence(const string& filename)
{
ifstream f(filename.c_str());
return f.is_open();
}
string getFileName()
{
string filename;
cout << "Please enter in the name of the file you'd like to open: ";
cin >> filename;
return filename;
}
void getFile(string filename, /*out*/ ifstream& file)
{
const bool file_exists = checkFileExistence(filename);
if (!file_exists) {
cout << "File " << filename << " not found." << endl;
filename = getFileName(); // poor style to reset input parameter though
ofstream dummy(filename.c_str();
if (!dummy.is_open()) {
cerr << "Could not create file." << endl;
return;
}
cout << "File created." << endl;
}
file.open(filename.c_str());
}
int main()
{
// ...
ifstream file;
getFile("filename.ext", file);
if (file.is_open()) {
// do any stuff with file
}
// ...
}
ifstream does not support copy construct semantics (that what the error message basically sais), so you cannot return an ifstream. Return an ifstream* instead, and pass to the caller the responsability to delete the allocate pointer.
As an option, ifstream may be extended and custom constructor added to new class.
I've extended it to create test resource stream, encapsulating test resource lookup inside of it.
// test_utils.h
class TestResourceStream : public std::ifstream {
public:
TestResourceStream(const char* file_path);
};
// test_utils.cpp
namespace fs = std::filesystem;
fs::path test_resource_path(const char* file_path) {
fs::path path{std::string{"tests/resources/"} + file_path};
if (!fs::exists(path))
throw std::runtime_error{std::string{"path "} +
fs::absolute(path).c_str() + " does not exist"};
return path;
}
TestResourceStream::TestResourceStream(const char* file_path)
:std::ifstream{test_resource_path(file_path).c_str()} {}
// usage in test
TEST_CASE("parse") {
std::list<GosDump::Expertise> expertises;
TestResourceStream stream("requests/page_response.json");
GosDump::Json::parse(expertises, stream);
REQUIRE(10 == expertises.size());
}