This question already has answers here:
Need help regarding saving variables via fstream, do I need to use vector?
(2 answers)
Closed 8 years ago.
I got this problem, where I want to make a Devicelist, which I do with a vector of Devices. But I can't make it read/write correctly.
This is my functions:
#include "Devicelist.h"
bool Devicelist::AddDevice(const char *deviceName, char *type)
{
Device tempDevice(deviceName, type, ++id_);
Devicelist_.push_back(tempDevice);
return true;
}
bool Devicelist::deleteDevice(const char *deviceName)
{
for (int i = 0; i < Devicelist_.size(); i++)
{
if (strcmp(deviceName, Devicelist_[i].getName()) == 0)
{
Devicelist_.erase(Devicelist_.begin() + i);
return true;
}
else
{
cout << "No Device found with that Devicename." << endl;
return false;
}
}
}
bool Devicelist::SaveToFile()
{
//remove("Devices.dat");
ofstream SaveFile("Devices.dat", ios::out | ios::binary);
if (!SaveFile)
{
cerr << "File could not be opened." << endl;
return false;
}
for (int i = 0; i < Devicelist_.size(); i++)
SaveFile.write((const char *)(&Devicelist_[i]), sizeof(Devicelist_[i]));
SaveFile.close();
return true;
}
bool Devicelist::LoadFromFile()
{
ifstream LoadFile("Devices.dat", ios::in | ios::binary);
if (!LoadFile)
{
cerr << "File could not be opened." << endl;
return false;
}
for (int i = 0; i < Devicelist_.size(); i++)
LoadFile.read((char *)(&Devicelist_[i]), sizeof(Devicelist_[i]));
LoadFile.close();
return true;
}
Device Devicelist::findDevice(const char *deviceName)
{
for (int i = 0; i < Devicelist_.size(); i++)
{
if (strcmp(deviceName, Devicelist_[i].getName()) == 0)
return Devicelist_[i];
else
cout << "Couldn't find device." << endl;
}
}
And this is my main:
#include "Devicelist.h"
void main()
{
Devicelist list;
list.AddDevice("Lampe3", "Lampe");
list.AddDevice("Lampe4", "Lampe");
list.SaveToFile();
Devicelist list2;
list2.LoadFromFile();
Device lampe = list2.findDevice("Lampe");
cout << lampe.getName() << endl;
cout << lampe.getID() << endl;
cout << lampe.getType() << endl;
}
Can anyone see what my problem is?
Thanks in advance!
Edit1:
My .h-file for Devicelist is:
#ifndef DEVICELIST_H
#define DEVICELIST_H
#include "Device.h"
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
class Devicelist
{
public:
Devicelist();
bool SaveToFile();
bool LoadFromFile();
bool AddDevice(const char *deviceName, char *type);
bool deleteDevice(const char *deviceName);
Device findDevice(const char *devicename);
private:
vector<Device> Devicelist_;
int id_;
};
#endif
This is my Device .h-file:
#ifndef DEVICE_H
#define DEVICE_H
#include <iostream>
#include <string>
#define DNAME_SIZE 33
using namespace std;
class Device
{
public:
Device(const char *devicename = "Default", char *type = "type", int id = 0);
const char *getName();
int getID();
int getType();
private:
char deviceName_[DNAME_SIZE];
int id_;
int type_;
};
#endif
And Device .cpp-file:
#include "Device.h"
Device::Device(const char *deviceName, char *type, int id)
{
strncpy_s(deviceName_, deviceName, DNAME_SIZE);
if (type == "Lampe")
type_ = 1;
else if (type == "Roegalarm")
type_ = 2;
else if (type == "Tyverialarm")
type_ = 3;
else
{
type_ = 0;
cout << "Type does not exists." << endl;
}
id_ = id;
}
const char *Device::getName()
{
return deviceName_;
}
int Device::getID()
{
return id_;
}
int Device::getType()
{
return type_;
}
The problem is when I try to read view what's saved in the file, this is the output:
http://imgur.com/P8WEAKq
The problem is here in the LoadFromFile function, I think:
for (int i = 0; i < Devicelist_.size(); i++)
LoadFile.read((char *)(&Devicelist_[i]), sizeof(Devicelist_[i]));
The DeviceList is empty, or has the wrong size.
You could write something similar as in AddDevice:
Device tempDevice();
LoadFile.read((char *)(&tempDevice), sizeof(tempDevice));
Devicelist_.push_back(tempDevice);
but the size (how many devices) of the file needs to be known.
As mentioned in a comment, this works only if Device doesn't have pointers to other objects for example. It should be a POD (Plain Old Data) object.
An elegant solution would be to implement streaming operators on Device
You will need to to have Device::StoreToFile write the number of devices before writing out each device. This will enable your Device::LoadFromFile to know how many device instances to read from the file.
I highly recommend you do not write a class or structure bit for bit to the file. If your class or struct has any advanced data structures, such as std::string or std::vector, you can't write them in binary. They may use pointers to memory, which will not be the same when you load them.
Here is a better solution:
1. Pass a buffer of uint8_t to the load and store methods.
2. The methods load from the buffer or store into the buffer (append).
3. The caller reads or writes the buffer to file.
4. The class also has a "size on file" method that returns the space occupied on a file, which may be different than the structure size.
Also search the web for "boost::serialization".
Related
#include <iostream>
#include <fstream>
using namespace std;
struct review {
string text;
string date;
};
void getRegistry(int i) {
review* reg = new review;
ifstream file;
file.open("test.txt", ios::binary);
if (file) {
file.seekg(i * sizeof(review), ios::beg);
file.read(reinterpret_cast<char*>(reg), sizeof(review));
cout << reg->text;
file.close();
}
delete reg;
}
void generateBinary()
{
ofstream arq("test.txt", ios::binary);
review x;
x.text = "asdasdasd";
x.date = "qweqweqwe";
for (int i = 1; i <= 1000000; i++)
{
arq.write(reinterpret_cast<const char*>(&x), sizeof(review));
}
arq.close();
}
int main() {
generateBinary();
getRegistry(2);
return 0;
}
Hello, I'm trying to make a program which writes several "reviews" to a binary file, then reads a certain registry. The program seems to work, but, in the end, it always throws an exception: "Exception thrown at 0x00007FF628E58C95 in trabalho.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF." How can I solve this? Thank you!
The problem is that you can't read/write std::string objects they way you are. std::string holds a pointer to variable-length character data that is stored elsewhere in memory. Your code is not accounting for that fact.
To be able to seek to a specific object in a file of objects the way you are attempting, you have to use fixed-sized objects, eg:
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
struct review {
char text[12];
char date[12];
};
void getRegistry(int i) {
ifstream file("test.txt", ios::binary);
if (file) {
if (!file.seekg(i * sizeof(review), ios::beg)) throw ...;
review reg;
if (!file.read(reinterpret_cast<char*>(®), sizeof(reg))) throw ...;
cout << reg.text;
}
}
void generateBinary()
{
ofstream arq("test.txt", ios::binary);
review x = {};
strncpy(x.text, "asdasdasd", sizeof(x.text)-1);
strncpy(x.date, "qweqweqwe", sizeof(x.date)-1);
for (int i = 1; i <= 1000000; ++i) {
if (!arq.write(reinterpret_cast<char*>(&x), sizeof(x))) throw ...;
}
}
int main() {
generateBinary();
getRegistry(2);
return 0;
}
Otherwise, to deal with variable-length data, you need to (de)serialize each object instead, eg:
#include <iostream>
#include <fstream>
#include <cstdint>
using namespace std;
struct review {
string text;
string date;
};
string readStr(istream &is) {
string s;
uint32_t len;
if (!is.read(reinterpret_cast<char*>(&len), sizeof(len))) throw ...;
if (len > 0) {
s.resize(len);
if (!is.read(s.data(), len)) throw ...;
}
return s;
}
void skipStr(istream &is) {
uint32_t len;
if (!is.read(reinterpret_cast<char*>(&len), sizeof(len))) throw ...;
if (len > 0) {
if (!is.ignore(len)) throw ...;
}
}
void writeStr(ostream &os, const string &s) {
uint32_t len = s.size();
if (!os.write(reinterpret_cast<char*>(&len), sizeof(len)) throw ...;
if (!os.write(s.c_str(), len)) throw ...;
}
review readReview(istream &is) {
review r;
r.text = readStr(is);
r.date = readStr(is);
return r;
}
void skipReview(istream &is) {
skipStr(is);
skipStr(is);
}
void writeReview(ostream &os, const review &r) {
writeStr(is, r.text);
writeStr(is, r.date);
}
void getRegistry(int i) {
ifstream file("test.txt", ios::binary);
if (file) {
while (i--) skipReview(file);
review reg = readReview(file);
cout << reg.text;
}
}
void generateBinary()
{
ofstream arq("test.txt", ios::binary);
review x;
x.text = "asdasdasd";
x.date = "qweqweqwe";
for (int i = 1; i <= 1000000; ++i) {
writeReview(arq, x);
}
}
int main() {
generateBinary();
getRegistry(2);
return 0;
}
The operator sizeof (review) does not return the length of containing strings. This is due to the fact that string class contain pointers to real strings, which are located in a separated location of the memory, allocated dynamically. You should use explicitly the length of strings, and write explicitly the strings instead of the class. Same thing with reading from file. Read strings first, then attribute to review.
I am writing some C++ code to create an item of a class i have created inside a vector of another class. I seem to be able to create the items inside the vector but when i try to read a variable of the item inside the vector i get the error
Exception thrown: read access violation.
_Right_data was 0x8.
inside the document xstring.
I think it might have something to do with me not actually creating each team inside the vector.
the code i have written that is relavent is
for (int x = 1; x <= mainLeague.getNumTeams(); x++) {
std::cout << "please enter the name of team " << x << ":";
std::getline(std::cin, currLine);
parsed = parseText(currLine, &posResponsesTeamNames);
if (parsed == 2) {
prepForEnd();
return 1;
}
else if (parsed == 0) goto enterTeamNames;
mainLeague.createTeam(currLine);
}
std::cout << mainLeague.getName(5);
}
#pragma once
#include "team.h"
#include <string>
#include <vector>
#include <iostream>
class league
{
std::vector<team*> teams;
int numTeams, numInitTeams = 0;
const float sysCon = 0.5;
public:
league(int a);
int getNumTeams();
void initVector(int numTeams);
void createTeam(std::string name);
std::string getName(int num);
};
void league::createTeam(std::string name)
{
if (numInitTeams < teams.size()) {
team currTeam = team::team(name);
teams.at(numInitTeams) = &currTeam;
numInitTeams;
}
else {
std::cout << "error max amount of teams already created";
}
}
#pragma once
#include<string>
class team
{
float RD;
int rating;
std::string name;
public:
team(std::string name);
team();
std::string getName();
};
std::string team::getName()
{
return team::name;
}
I'm struggling with implementation of a function that I define in my header file of my c++ program. I think I am misunderstanding how it works, but most reading on the web doesn't spell it out clear enough for my peon brain to understand.
I'm trying to make a 'sort_name' function that sorts an array of private classes based on the c-string "name" when the function is invoked.
Unfortunately I keep running into errors when trying to use it.
Here's my courses_main.cpp's main function:
int main()
{
Course* courses[10] = {};
int selection;
int size = 0;
do
{
selection = menu();
if (selection == 1)
{
if (size < 10)
add(courses, size);
else
std::cout << "\nUnable to add more classes.";
}
else if (selection == 2)
{
edit(courses, size);
}
else if (selection == 3)
{
}
else if (selection == 4)
{
sort_name(courses, size);
for (int i = 0; i < size; i++)
{
courses[i]->display();
}
}
else if (selection == 5)
{
}
else if (selection == 6)
{
}
else if (selection == 7)
{
break;
}
else
{
std::cout << "\nInvalid selection.";
}
} while (selection != 7);
std::cout << "\nPress any key to exit.";
(void)_getch();
return 0;
}
Here is my courses_functions.cpp where I define the sort_name function:
void swap_ptrs(Course*& pt1, Course*& pt2) //Passes the pointers by reference
{
Course* tmp = pt1;
pt1 = pt2;
pt2 = tmp;
}
void Course::sort_name(Course* co_ptr[], int size) //has to be apart of the class (Course::) to have access to the name data
{
bool swap;
do
{
swap = false;
for (int i = 0; i < size - 1; i++)
{
if (strcmp(co_ptr[i]->name, co_ptr[i + 1]->name) > 0) //We're now comparing and swapping pointers
{
swap_ptrs(co_ptr[i], co_ptr[i + 1]);
swap = true;
}
}
} while (swap);
}
And here is my courses.h header where I define(?) the function:
#ifndef COURSE_H
#define COURSE_H
#include <iostream>
#include <conio.h>
#include <iomanip>
#include <stdio.h>
#include <string.h>
#include <ctime>
#include <fstream>
#include <cstdlib>
class Course
{
private:
char name[10] = "", grade;
int units;
public:
Course()
{
name;
grade;
units = 0;
}
void read() //Initializes course and collects information from user
{
std::cout << "\nEnter course name: ";
std::cin.getline(name, 10, '\n');
std::cout << "\nEnter number of units: ";
std::cin >> units;
std::cout << "\nEnter grade received: ";
std::cin >> grade;
std::cin.ignore();
}
void display() const //Displays course to user
{
std::cout << name << ' ' << units << ' ' << grade << std::endl;
}
~Course() //Destructor frees allocated dynamic memory
{
std::cout << "\nDeleting any dynamically created object";
}
void sort_name(Course* co_ptr[], int size);
};
#endif // COURSE_H
I'm not understanding much about classes outside of how they're extremely similar to structs, so any direction would be welcome thanks!
A better code organization would be to declare the functions in the .h file and implement them in the.cpp.
Here is a working sample without .cpp for simplification. Only Courses.h and main.
With .cpp your program should be somethong like this:
Course.h
#ifndef COURSE_H
#define COURSE_H
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std; //<-- for test pusposes, you should use std:: scope
class Course
{
private:
string name;
int units, grade;
public:
Course(); //<-- the code you have inside the constructor, only units = 0,
// does somenthing, you should initialize all the members.
Course(string name);
void read();
void display() const;
~Course(); //<-- to dealocate dynamic memory you need to really dealocate it with delete.
string getName() const;
};
#endif // COURSE_H
And in your .ccp the implementation:
Course.cpp
#include "Course.h"
Course::Course(){ /*do stuff*/ }
Course::Course(string name) : name(name) { /*do stuff*/ } //<-- initializing name here
void Course::read() {/*do stuff*/ }
void Course::display() const {/*do stuff*/ }
Course::~Course() {/*do stuff*/ }
string Course::getName() const { return name; }
For sorting you don't need anything fancy you have sorting tools and data structures in the C++ libraries that make your job easy, like vector for objects container and sort for sorting.
Main
#include "Course.h"
bool sorting(Course course1, Course course2) { //conditional function for sort (#include <algorithm>)
return course1.getName() < course2.getName();
}
int main() {
vector<Course> courses = { Course("zed"), Course("albert")}; // adding courses
courses.push_back(Course("mary")); // adding some more
courses.push_back(Course("john"));
courses.push_back(Course("ana"));
courses.push_back(Course("charles"));
sort(courses.begin(), courses.end(), sorting); //<-- sorting
for (Course c : courses) {
cout << c.getName() << " ";
}
}
Output:
albert ana charles john mary zed
I am fairly new to c++ and building a simple battleship game. I have a function that initializes the ship types and lengths by taking in a vector of ship objects. I have confirmed that the type and ship length are updating inside the function but the changes are not persisting in the main function. Any help would be greatly appreciated.
#include <iostream>
#include <vector>
#include "ships.h"
#include "board.h"
#include "player.h"
using std::vector;
void initialize_ships(vector<ship> &newFleet)
{
vector<string> ship_types = { "carrier","battleship","submarine","cruiser","destroyer" };
for (auto ii = 0; ii != newFleet.size(); ++ii)
{
newFleet[ii].type = ship_types[ii];
newFleet[ii].setLength();
if (newFleet[ii].type == "carrier")
{
std::cout << "Carrier in function has type: " << newFleet[ii].type << std::endl;
}
}
}
int main()
{
ship player_carrier, player_battleship, player_submarine, player_cruiser, player_destroyer;
vector<ship> player_fleet = {player_carrier, player_battleship, player_submarine, player_cruiser, player_destroyer };
initialize_ships(player_fleet);
std::cout << "Carrier in main has type: " << player_carrier.type << std::endl;
system("pause");
return 0;
}
#ifndef SHIPS_H
#define SHIPS_H
#include <iostream>
#include <string>
using std::string;
struct ship
{
// ships member variables
int len;
char dir;
int health;
int row;
int col;
char symb;
string type;
void setLength()
{
if (type == "carrier")
{
len = 5;
}
else if (type == "battleship")
{
len = 4;
}
else if (type == "cruiser")
{
len = 3;
}
else if (type == "submarine")
{
len = 3;
}
else if (type == "destroyer")
{
len = 2;
}
else
{
std::cout << "Invalid ship type!" << std::endl;
}
}
};
#endif // SHIPS_H
Accidentally making copies is a common reason for changes to be “lost”. For this particular situation, where you want to have objects in a container and have names for those objects (as if they were members of a class), the sensible thing to do is to make references to objects in the container:
vector<ship> player_fleet(5);
ship &player_carrier=player_fleet[0],
&player_battleship=player_fleet[1],
&player_submarine=player_fleet[2],
&player_cruiser=player_fleet[3],
&player_destroyer=player_fleet[4];
Be careful not to cause the vector to reallocate, since that would invalidate your references. (Of course, if it never changes size, ship player_fleet[5]; would suffice.)
I am writing a C++ program (see below). My goal is to store data in iov struct. I have allocated buffer of fixed length in constructor. Every time that buffer gets filled, I want to transfer data in iov and allocated new buffer of fixed length. Finally when done with data processing, I want to return iov struct. My intension here is to store all these data into iov so that if it's required in future, I can send data easily. I have written sample code. But it seems it's not working. I got an "Bus error: 10". Can someone help me?
Sample code:
#include <iostream>
#include <string>
#include <sys/uio.h>
#include <cstdlib>
using namespace std;
#define MAX_LEN 1000
#define MIN_LEN 20
class MyClass
{
public:
MyClass();
~MyClass();
void fillData(std::string &data);
private:
struct iovec *iov;
unsigned int count;
unsigned int len;
char *buf;
unsigned int total_len;
unsigned int tmp_len;
};
MyClass::MyClass()
{
cout << "Inside constructor" << endl;
total_len = MIN_LEN;
buf = (char *)malloc(MAX_LEN);
if (buf == NULL) {
cout << "Error: can’t allocate buf" << endl;
exit(EXIT_FAILURE);
}
}
MyClass::~MyClass()
{
free(buf);
}
void MyClass::fillData(std::string &data)
{
unsigned int d_len, tmp_len, offset;
d_len = data.size();
const char* t = data.c_str();
total_len += d_len;
tmp_len += d_len;
if (total_len > MAX_LEN) {
/* Allocate memory and assign to iov */
tmp_len = d_len;
}
memcpy(buf + offset, t, d_len);
/* Adjust offset */
}
int main()
{
MyClass my_obj;
int i;
std::string str = "Hey, welcome to my first class!";
for (i = 0; i < 10; i++) {
my_obj.fillData(str);
}
return 0;
}
Without understanding the intent of your program in detail, it is very clear that you forgot to reserve memory for the iov-objects themselfes.
For example, in your constructor you write iov[0].iov_base = buf, yet iov has not been allocated before.
To overcome this, somewhere in your code, before the first access to iov, you should write something like iov = calloc(100,sizeof(struct iovev)) or a c++ equivalent using new[].
Consider the following program:
struct myStruct {
char *buf;
int len;
};
int main() {
struct myStruct *myStructPtr;
myStructPtr->buf = "Herbert"; // Illegal, since myStructPtr is not initialized; So even if "Herbert" is valid, there is no place to store the pointer to literal "Herbert".
myStructPtr[0].buf = "Herbert"; // Illegal, since myStructPtr is not initialized
// but:
struct myStruct *myStructObj = new (struct myStruct);
myStructObj->buf = "Herbert"; // OK, because myStructObj can store the pointer to literal "Herbert"
myStructObj->buf = "Something else"; // OK; myStructObj can hold a pointer, so just let it point to a different portion of memory. No need for an extra "new (struct myStruct)" here
}
I took your code, which didn't exactly use anything with the iovec, and I modified it a little.
I am not sure why developers prefer buffers of char* instead of std::string
or why use a pointer which should be allocated and then deleted instead of using a std::vector
I also added a function which uses the iovec. It is called void MyClass::print_data(). It prints all the data in the vector iovecs
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/uio.h>
using namespace std;
class MyClass
{
vector<struct iovec> iovs;
vector<string> bufs;
public:
MyClass();
~MyClass();
void fill_data(const string &data);
void print_data();
};
MyClass::MyClass()
{
cout << "Inside constructor" << endl;
}
MyClass::~MyClass()
{
}
void MyClass::fill_data(const string &data)
{
stringstream stream;
stream << setw(2) << setfill(' ') << (this->bufs.size() + 1) << ". "
<< data << endl;
this->bufs.push_back(stream.str());
iovec iov = {&(bufs.back()[0]), bufs.back().size()};
this->iovs.push_back(iov);
}
void MyClass::print_data() {
writev(STDOUT_FILENO, iovs.data(), iovs.size());
}
int main() {
MyClass my_obj;
string str = "Hey, welcome to my first class!";
for (int i = 0; i < 10; ++i) {
my_obj.fill_data(str);
}
my_obj.print_data();
return 0;
}
compile it like so: g++ test.cpp