I am trying to create class that reading and writing to the same file in c++ using template function and I'm trying to realize the function read() that reading a char or int and returns it and when i tried to run it i got number like -998324343 please help :)
#include<iostream>
#include<fstream>
using namespace std;
template <class T>
class myFile
{
ifstream in;
ofstream out;
public:
myFile(char* fileName)
{
in.open(fileName);
if (!in.is_open())
throw"couldnt open file to reading";
out.open(fileName);
if (!out.is_open())
throw"couldnt open file to writing";
cout << read();
}
T read() {
T x;
in >> x;
return x;
}
};
int main()
{
try {
myFile<int> a("read.txt");
}
catch (char* msg) {
cout << msg << endl;
}
}
Your out and in refer to the same file. So when this happens:
in.open(fileName);
if (!in.is_open())
throw"couldnt open file to reading";
out.open(fileName);
Assuming fileName exists as a file, out will truncate the file, so it becomes empty. The subsequent in >> x; will fail (because the file is empty) and depending on the C++ standard you're compiling against, either x will get zeroed out (since C++11) or remain unmodified (until C++11). I'm assuming you're compiling pre-C++11, in which case what you see is whatever indeterminate value x was initialized with.
Not sure what you need out for, but you either want it to refer to a different file or open it in append mode.
Regardless of whether or not out is truncating the file, the >> operation can fail. If it fails, you will get garbage data (or 0). So you need to check the result of that operation.
Note: Everywhere you are using char* you should be using const char*. The conversion from string literal to char* is deprecated (if you compiled with warnings enabled, you would see this).
Related
I would like to open a file for writing, such that the file is wiped and written over at the first write instruction. That is, the file should retain its contents when opened, and only at the first insertion is actually written over.
Using std::fstream, if I use the mode std::ios_base::out, the file is immediately wiped as soon as the constructor is called
#include <fstream>
int main()
{
std::fstream f("test.txt", std::ios_base::out);
// Now test.txt is empty
f << "hello";
}
This behavior would be ok if test.txt does not exists. But if test.txt exists, I would like to have its contents preserved until I write to it. If I open it with
std::fstream f("test.txt", std::ios_base::out | std::ios_base::in);
indeed an existing file is still preserved, and it is written over only on f << "hello".
However, if the file exists and contains more data then the string "hello", then f << "hello" will only overwrite the first 5 characters. I would like the file to be wiped, as if it were opened with std::ios_base::out, but only when I execute f << "hello".
Context
The problem arises with a routine like this:
void do_something(std::ostream& s)
{
//... very long computation
s << "some infos" << std::endl;
}
I call do_something passing to it a stream, which could or could not be a file. If it is a file, I would like to be able to inspect test.txt before the computation is done.
One possibility could be to wrap a fstream in a new object that actually opens the file only when operator<< is called, something like
#include <fstream>
class myfstream {
std::fstream m_f;
std::string m_filename;
public:
myfstream(std::string& filename) : m_filename{filename} { }
template <typename U>
myfstream& operator<<(const U& s) { if (!m_f.is_open()) m_f.open(m_filename, std::ios_base::out); m_f << s; return *this; }
};
The drawback of this solution is the lack of flexibility: if the routine do_something does something else than just calling operator<<, one has to define all the other members of std::ostream, to mimick its behavior. Also, to retain flexibility do_something has to be declared as
template <typename S>
void do_something(S& o);
Is there a better solution? Would it be possible, say, given a fstream to delete all data after the current position of the output indicator?
Update
Apparently, a possible solution is to get the actual write position with tellp() and after closing the file, resize it. See following code:
#include <fstream>
#include <filesystem>
#include <iostream>
int main()
{
typename std::fstream::pos_type size;
{
char d;
std::fstream f("test.txt", std::ios_base::in | std::ios_base::out);
std::cin >> d; // Just to pause
f << "hello";
size = f.tellp();
}
char d;
std::cin >> d; // Just to pause
std::filesystem::resize_file("test.txt", size);
return 0;
}
(The std::cin are there only to pause and be able to check the actual contents of the file).
Testing the program, after the first "pause" the file is still intact. After the second pause, the first characters have been overwritten with the string "hello", but any text following that is still untouched. Finally, resize_file shrinks the file to the actual length of the written string.
The above code does an implicit conversion between a pos_type as returned from tellp() and a std::uintmax_t as required from resize_file.
Is this conversion safe?
so basically I was trying to save a class inside a .dat file but in my code but it says this error No matching member function for call to 'open' but I put fstream header. I don't know if I'm writing something wrong. I use Xcode 10.
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
memberinformation()
{ name="not assigned" ;
phonenumber=0;
memberid=0;
}
int option3();
int option2();
int option1();
int option4();
};
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app,ios::binary) //this is where I get the error.
f.write((char*)&k,sizeof(k));
}
You are lucky to have been stopped by a simple error. #Alex44 has already shown how to get rid of the error:
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
But the following line is even worse:
f.write((char*)&k,sizeof(k));
because the compiler will not show any error, while the content of the string will not be saved in the file. std::string is not trivially copiable and because of that, the memberinformation class is not either. So you should not try to write it to a file as raw bytes.
You should instead write a serialization function that writes to a binary stream (just a possible serialization way):
phonenumber as a long int (no problem there)
memberid as an int (no problem there)
name.size as a size_t
name.data as name.size bytes
The other two answers have answered:
Why its not compiling.
Why its a bad idea to write binary objects.
I would suggest that you serialize the object via the standard C++ technique of using the stream operators. This makes writting/reading the objects trivial and usually makes debugging problems easy.
Using the format suggested by #serge-ballesta in his post:
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
// OLD STUFF GOES HERE
void swap(memberinformation& other) noexcept
{
using std::swap;
swap(name, other.name);
swap(phonenumber, other.phonenumber);
swap(memberid, other.memberid);
}
friend std::ostream& operator<<(std::ostream& str, memberinformation const& data)
{
return str << data.phonenumber << " "
<< data.memberid << " "
<< data.name.size() << " "
<< data.name << " ";
}
friend std::istream& operator<<(std::istream& str, memberinformation& data)
{
memberinformation tmp;
std::size_t nameSize
if (str >> tmp.phonenumber >> tmp.memberid >> nameSize) {
// All sizes were read correctly.
tmp.name.resize(nameSize);
if (str.ignore(1).read(&tmp.name[0], nameSize)) {
// ignored the space and read the name correctly.
// So now we update the "data" object
tmp.swap(data);
}
}
return str;
}
};
Now in your code:
int main()
{
memberinformation object;
std::cout << object;
std::cin >> object;
std::ofstream file("Data.dat");
file << object;
}
You miss a semicolon and you need to "bitwise or" your flags:
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
...
}
The answers above address your initial problem. I'm going to talk about two more.
First, you probably should f.close() at the end of your method. It may be perfectly fine to let it drop out of scope and clean up from there, but I personally think that's ugly, and I wouldn't count on it.
Second, I wouldn't store the data in binary unless there's a really good reason to do it. It won't be portable. Serge above suggests a serialization method. I'd consider an alternate approach. I'd write to the file in a human readable form such as JSON. Yes, it's a little more work, but...
-If you change your class, your old files will still be readable
-They are portable across environments
-You can actually look at them and readily understand what they contain
So Serge's suggestions above aren't horrible, but I'd pick a more modern serialization / deserialization style.
Note that your f.write won't work because your object contains other objects, you don't know how they work under the hood. That string, for instance, almost certainly can't be dumped the way you're trying to do it. Plus you aren't only dumping your data.
Also, you should printf the sizeof(k). You might find it interesting information. Try to account for every byte. You could printf the sizeof(k.name) to help you work some of it out.
I'm almost positive the information doing so would surprise you, but I haven't actually done it myself, because I would never try to raw memory copy C++ objects, and that's in effect what you're trying to do.
I am trying to reading and write objects to a file in C++, writing the object works fine, reading gives segmentation core dump. I have commented the code for writing objects to file, while writing we can uncomment that part and comment the reading part.
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
class RelianceMart{
string name;
double trolley_number;
public:
RelianceMart(){
name = "NA";
trolley_number = 0;
}
RelianceMart(string name, double trolley_number){
this->name = name;
this->trolley_number = trolley_number;
}
void setname(string name){
this->name = name;
}
string getname(){
return name;
}
void settrolleynumber(double trolley_number){
this->trolley_number = trolley_number;
}
double gettrolleynumber(){
return trolley_number;
}
};
int main(){
string name;
double trl_num;
RelianceMart mart[3];
RelianceMart obj;
// ofstream fout("PersistentStorage.txt");
/*
for(int i=0;i<3;i++){
cin>>name;
cin>>trl_num;
mart[i] = RelianceMart(name, trl_num);
fout.write((char *) & mart[i], sizeof(mart[i]));
}
fout.close();
*/
ifstream fin("PersistentStorage.txt");
while(!fin.eof()){
fin.read((char *) & obj,sizeof(obj));
cout<< obj.getname();
}
fin.close();
return 0;
}
The members of std::string is really nothing more than a member variable for the length, and a member variable being a pointer to the actual string contents.
Pointers are private and unique to a specific process in all modern protected multi-tasking operating systems, no other process (not even one started from the same program) can reuse the same pointer.
When you write the RelianceMart objects, you write the pointer of the name string object to the file. As mentioned above no other process can use this pointer, and therefore can't read the file.
Furthermore when you attempt to read the raw objects, you read raw data overwriting the existing data in the constructed object, and the object won't be properly constructed anymore.
You also don't open the file in binary mode, which is wrong since you write and read raw binary data, not text.
The common solution is to use serialization, and the most common way to do it is simply to overload the "output" and "input" operators << and >>.
In the overloaded functions you simply write and read each object as text, again using the formatted << and >> operators.
Lastly, please read Why is iostream::eof inside a loop condition considered wrong?
I would use a serialization framework, you could use Google's Protocol Buffers(https://developers.google.com/protocol-buffers/).
If you consider a fullblown framework overkill, you can always write your own serialization framework, I've done that, I did use the JSON-format to encode the object.
suppose I want to write in a .txt file in following format
start-----
-----A----
----------
-B--------
-------end
I've 3 functions that write the parts to file; start to A, A to B then B to end.
My function call are going to be in this order
Func1(starts writing from start of file)
{ }
Func2(needs pointer to position A for writing to file)
{ }
Func3(needs pointer to position B for writing to file)
{ }
Take Fun1 and Func2 for example, Func1 will end writing at A, but the problem is that Func2 needs to go forward from point A. How can I pass a pointer of position A to Func2 so that it'll be able to continue writing from position A in the file?
Since this is c++ we could use file stream object from the standard c++ library.
#include <iostream>
#include <fstream>
using namespace std;
void func1(ofstream& f)
{
f << "data1";
}
void func2(ofstream& f)
{
f << "data2";
}
int main () {
ofstream myfile ("example.txt");
if (myfile.is_open())
{
func1(myfile);
func2(myfile);
myfile.close();
}
else cout << "Unable to open file";
return(0);
}
However this approach is universal. When you are working with file, you get some file identificator. It could be a FILE struct, Win32 HANDLE etc. Passing that object between functions will allow you to continuously write the file.
Not sure how you're outputting to a file (using which output method), but normally, the file pointer keeps track itself where it is up to.
eg using fstream
ofstream outFile;
outFile.open("foo.txt");
if (outFile.good())
{
outFile<<"This is line 1"<<endl
<<"This is line 2"; // Note no endl
outFile << "This is still line 2"<<endl;
}
If you pass the outFile ofstream object to a function, it should maintain position in the output file.
Previously answered: "ofstream" as function argument
as the title suggests, I am having a problem with not being able to read from an input file after passing the ifstream object to a class function. Basically I'm trying to sort a list of numbers using a heap ADT implemented with an array.
int main() {
ifstream infile("input.txt");
HeapSort* heap = new HeapSort(20); // creates a heap (array) with size 20
heap->buildHeap(&infile);
return 0;
}
void HeapSort::buildHeap(ifstream* infile) {
int data;
while (infile >> data) {cout << data << endl;}
infile->close();
}
the error occurs in the conditional of the while loop inside buildHeap. The compiler can't recognize the operator ">>" between an 'int' and an 'ifstream' object. However, strangely enough, if I write that same while loop inside main(), it'll work just fine. Also of note is that if I remove the while loop, the compiler returns no errors. Meaning, simply the act of passing the ifstream object from main to buildHeap is OK.
Please avoid suggesting alternative ways of achieving this. I was asked to not use any special fstream functions like eof(). I can only use the ">>" operator to read from the desired file.
You're passing a pointer to a stream, so you need to dereference it:
while (*infile >> data)
If you want your code to look like what you say you did in main, then you pass a reference:
heap->buildHeap(infile);
//...
void HeapSort::buildHeap(ifstream& infile)
{
int data;
while (infile >> data) { ... }
infile.close();
}