File writing and appending in Binary mode not working - c++

I am trying to append into file in binary mode but the logic below is not working.
For Pdf files,file is getting corrupted and for text files, it is adding some junk data in addition to my file contents.
My variable m_strReceivedMessage is of type std::string.
std::ofstream out(file, std::ios::binary | std::ios_base::app );
int i = sizeof(m_strReceivedMessage);
if (out.is_open()) {
// out.write(m_strReceivedMessage.c_str(), m_strReceivedMessage.size());
//out << m_strReceivedMessage;
out.write(reinterpret_cast<char *>(&m_strReceivedMessage), m_strReceivedMessage.size());
}

You're printing the memory of the std::string object, rather than the character buffer that it contains. To get a pointer to the character buffer, see the data() member function. Hint: The fact that you need to cast std::string* using reinterpret_cast<char*> is a dead giveaway that you're doing something very wrong.
Also, I'm not familiar with the PDF spec, but I suspect that it may possibly contain nul bytes. And depending on how you get your std::string, it's possible you may have missed any content after the first nul. std::vector<char> would be more appropriate way to store binary data.

Related

Why some data in binary file is shown as it is and other is shown in a strange way

I have code, which writes vector of such structures to a binary file:
struct reader{
char name[50];
int card_num;
char title[100];
}
Everything works actually fine but when I, for example, write to file structure {One,1,One} and open .txt file, where it is stored, I see this:
One ММММММММММММММММММММММММММММММММММММММММММММММММ One ММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММММ
So I was asked why is it displayed so, what it depends on, but I could'nt give a good answer to that question
EDITED:
Added code which I use to write to file
void Write_to_File(vector<reader>& vec){
cin.clear(); // clearing
fflush(stdin);// input stream
const char* pointer = reinterpret_cast<const char*>(&vec[0]);
size_t bytes = vec.size() * sizeof(vec[0]);
fstream f("D:\\temp.txt", ios::out);
f.close();
ofstream file("D:\\temp.txt", ios::in | ios::binary);
file.write(pointer, bytes);
file.close();
remove("D:\\lab.txt");
rename("D:\\temp.txt", "D:\\lab.txt");
cout << "\n*** Successfully written data ***\n\n";
}
P.S. When I read from file everything is ok
You write 154 octets in a file, only One and One are char, so your text editor try to read char but get mostly garbage. You write binary, you should not expect to have something readable.
Why some data in binary file is shown as it is and other is shown in a strange way
It seems that you are trying to read the binary data as if it contained character encoded data. Some of it does - but not all. Perhaps this is why you think that it seems strange. Other than that, the output seems perfectly reasonable.
why is it displayed so
Because that is the textual representation of the data that the object contains in the character encoding that your reader uses.
what it depends on
It depends on the values that you have initialized the memory to have. For example the first character is displayed as O because you have initialized name[0] with the value 'O'. Some of the data is padding between members that can not be initialized directly. What the value of those bytes depends on is unspecified.

Read .part files and concatenate them all

So I am writing my own custom FTP client for a school project. I managed to get everything to work with the swarming FTP client and am down to one last small part...reading the .part files into the main file. I need to do two things. (1) Get this to read each file and write to the final file properly (2) The command to delete the part files after I am done with each one.
Can someone please help me to fix my concatenate function I wrote below? I thought I had it right to read each file until the EOF and then go on to the next.
In this case *numOfThreads is 17. Ended up with a file of 4742442 bytes instead of 594542592 bytes. Thanks and I am happy to provide any other useful information.
EDIT: Modified code for comment below.
std::string s = "Fedora-15-x86_64-Live-Desktop.iso";
std::ofstream out;
out.open(s.c_str(), std::ios::out);
for (int i = 0; i < 17; ++i)
{
std::ifstream in;
std::ostringstream convert;
convert << i;
std::string t = s + ".part" + convert.str();
in.open(t.c_str(), std::ios::in | std::ios::binary);
int size = 32*1024;
char *tempBuffer = new char[size];
if (in.good())
{
while (in.read(tempBuffer, size))
out.write(tempBuffer, in.gcount());
}
delete [] tempBuffer;
in.close();
}
out.close();
return 0;
Almost everything in your copying loop has problems.
while (!in.eof())
This is broken. Not much more to say than that.
bzero(tempBuffer, size);
This is fairly harmless, but utterly pointless.
in.read(tempBuffer, size);
This the "almost" part -- i.e., the one piece that isn't obviously broken.
out.write(tempBuffer, strlen(tempBuffer));
You don't want to use strlen to determine the length -- it's intended only for NUL-terminated (C-style) strings. If (as is apparently the case) the data you read may contain zero-bytes (rather than using zero-bytes only to signal the end of a string), this will simply produce the wrong size.
What you normally want to do is a loop something like:
while (read(some_amount) == succeeded)
write(amount that was read);
In C++ that will typically be something like:
while (infile.read(buffer, buffer_size))
outfile.write(buffer, infile.gcount());
It's probably also worth noting that since you're allocating memory for the buffer using new, but never using delete, your function is leaking memory. Probably better to do without new for this -- an array or vector would be obvious alternatives here.
Edit: as for why while (infile.read(...)) works, the read returns a reference to the stream. The stream in turn provides a conversion to bool (in C++11) or void * (in C++03) that can be interpreted as a Boolean. That conversion operator returns the state of the stream, so if reading failed, it will be interpreted as false, but as long as it succeeded, it will be interpreted as true.

How to push binary data into std::string?

Im trying to create a binary file in the following way:
string buf;
...
buf += filename.length();
buf += filename;
etc. So first i give the length in binary format, but how do i convert this into a 4 byte char array, or 2 byte etc? basically i want to achieve the same functionality as this would:
int len = filename.length();
fwrite(&len, sizeof(len), 1, fp);
Which works fine, but having it in one string might be easier to process.
Edit: i dont want to use streams, nor vector, im trying to find out if its possible with strings.
Streams are the way to go. Not strings.
Use a vector for holding the data, or write it straight to the file (via streams)
simply use std:vector<unsigned char> and use a istream or ostream iterator to read/write data to/from the vector. For instance to read from a file you can do:
vector<unsigned char> binary_buffer;
ifstream in_file("my_binary_file.bin", ios_base::binary | ios_base::in);
istream_iterator<unsigned char> end_of_file;
istream_iterator<unsigned char> in_file_iter(in_file);
while (in_file_iter != end_of_file)
{
binary_buffer.push_back(*in_file_iter++);
}
Output would be even simpler:
ofstream out_file("another_binary_file.bin", ios_base::binary | ios_base::out);
ostream_iterator<unsigned char> binary_output(out_file);
copy(binary_buffer.begin(), binary_buffer.end(), binary_output);
Yes, it is possible to do this, because this is C++, and everything is possible in C++.
First, here is how you do it, then I'll answer the question of why you might need to do it:
std::ifstream input( "my.png", std::ios::binary );
std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(input), {});
int buffSize = buffer.size();
std::string myString(buffer.begin(), buffer.end());
In my case, I was using a framework where the HTTP Client only supported a string for post message body, but I needed to post raw binary of a file to the service I was using. I could either mess around with the internals of the library (bad) or introduce another unnecessary dependency for a simple file post (also not so good).
But since strings can hold this data, I was able to read the file, copy it to a string and then upload.
This is an unfortunate situation to be in, either way, but in some cases it is helpful to be able to do. Usually you would want to do something different. This isn't the clearest for someone else to read, and you will have some performance penalties from the copy, but it works; and it helps to understand the internals of why it works. std::string saves the data contiguously internally in binary format, like vector does, and can be initialized from iterators in the vector.
unsigned char is one byte long, so it is like a byte value. The string copies the bytes into itself, so you end up with the exact data.

C++ character array allocation error

I have a function designed to get a file's contents:
bool getFileContents(std::string loc, std::string &code) {
std::ifstream file(loc.c_str());
if(!file.is_open())
return err("Source file could not be read");
int length;
file.seekg(0, std::ios::end);
length = file.tellg();
file.seekg(0, std::ios::beg);
char *buffer = new char[length];
file.read(buffer, length);
code = buffer;
delete[] buffer;
file.close();
return true;
}
When I run this function, the file's length is always retrieved accurately. However, if I call the function once with a file, call it again with a nonexistent file, then call it one more time with the original file, the character string 'buffer' is larger than the int 'length'.
Well, that may not be accurate, rather - when the string 'buffer' is copied to the string 'code', 'code' is longer than 'length'. 'code' is, in each instance, instantiated immediately before the call to 'getFileContents', so it's not a matter of a previous value.
This also seems to occur if I retrieve the contents of a file, subsequently add or remove some text from the file, and retrieve the same file's contents again.
I have little experience with character strings, and figure that I'm not using them correctly, but the code I'm using came from an example, and I can't for the life of me find anything wrong with it.
Thanks for any help,
Wyatt
Well, the problem is that code = buffer relies on a NUL (\0) character to know where the buffer ends. You may be getting the NUL character by chance sometimes (esp. when the program has just started), but not always. Hence the intermittent behaviour.
Try replacing code = buffer with code = std::string(buffer, length).
Apart of the \0 problem described by aix, you do double allocation, which is not necessary here and unsafe (it might be an exception before delete, and you'll have a memory leak). Instead, you can allocate the buffer inside the string, as follows:
code.resize(length);
file.read(&code[0], length);
And don't forget to check the return value of read. It is not guaranteed that all length bytes will be read in one step.

binary read/write runtime failure

I've looked at binary reading and writing objects in c++ but are having some problems. It "works" but in addition I get a huge output of errors/"info".
What I've done is
Person p2;
std::fstream file;
file.open( filename.c_str(), std::ios::in | std::ios::out | std::ios::binary );
file.seekg(0, std::ios::beg );
file.read ( (char*)&p2, sizeof(p2));
file.close();
std::cout << "Name: " << p2.name;
Person is a simple struct containing string name and int age. When I run the program it outputs "Name: Bob" since I have already made a program to write to a file (so the object is already in filename).
IN ADDITION to outputting the name it also outputs:
* glibc detected * program: double free og corruption (fastttop): ***
Backtrace:
...
Memory map:
...
Abort
Is the name string in the Person struct a character array or a STL string? You can't fill in an STL String by binary reading data over top of it, since the data format is not serializable (contains pointers)
It would be interesting to see how you write the information to file as well, as well as how the Person struct is built.
If you don't have any problem that the file is plain text, my suggestion would be to write to file using string::c_str() (which returns a const char*) as well as using itoa() or itoa_s() to get the integer as a char*.
You can also have one or several constructors in Person:
Person(const std::string& name, int age);
Person(const char* name, int age);
then, when you extract the data from the file you just call the constructor with that data.
Either p2.name is a char* and you are writing and reading the pointer value, not what is pointed by it. Or p2.name is a more complex type such as std::string which is using internaly pointers with the same problem.
Serializing classes often need more work than just dumping the memory representation.
You said you wrote the Person object to a file. Did you tried to use a dump tool to see if what you have inside the file is what you are expecting?
Also did you tried to instead of using string, use a ordinary char (as #bdk pointed out) ?
When you use binary IO, the size must be fixed. If you use STL string here, it would have a problem as the size of a STL string is arbitrary.