Currently i have a program that loads binary data into a stringstream and then pases the data to a fstream like so:
stringstream ss(stringstream::binary | stringstream::in | stringstream::out);
ss.write(data, 512); // Loads data into stream
// Uses a memory block to pass the data between the streams
char* memBlock = new char[512];
ss.read(memBlock, 512);
ofstream fout("someFile.bin", ios::binary);
fout.write(memBlock, 512); // Writes the data to a file
fout.close();
delete[] memBlock;
My question is: is there a better way to pass the binary data between the streams?
Use the streambuf members, that's what they are for:
fout << ss.rdbuf();
Related
I'm trying to split and combine a binary file, for a reason not related to this question i'm using protobuf to store the files char* in a protobuf byte type
The code to serialize char* looks like this:
char* buffer = new char[buffer_size];
ifstream fileStream;
fileStream.open(fileName,ios::in | ios::binary);
//stuff here
Data *data = new Data(); // Protobuf Constructor
fileStream.read(buffer, buffer_size);
data->set_payload(buffer);
data->set_piece_no(piece_no);
.proto file :
syntax = "proto3";
message Data {
int32 piece_no = 1;
bytes payload = 2;
}
Then i try to combine all the pieces like so :
ofstream fileOutput;
fileOutput.open("out.bin", ios::out | ios::binary);
fileOutput << data->payload();
But sadly this doesn't work and the binary file generated is substantially smaller than the original.
I then suspect that the bytes could have null characters \0, and as a result the bytes have actually been truncated.
To test out my hypothesis i do the following:
Data *data = new Header();
data->set_payload("hel\0lo");
data->set_piece_no(piece_no);
ofstream fileOutput;
fileOutput.open("out.bin",ios::out | ios::binary);
fileOutput << data->payload();
Opening the binary file in a text editor (vscode) shows the following:
hel
But the following code:
string data("hel\0lo",6);
ofstream fileOutput;
fileOutput.open("out.bin", ios::out | ios::binary);
fileOutput << data;
Shows the following:
hel?lo
How can i output exactly what i inputted into protobuf, without any truncation because of arbitrary null bytes ?
If you pass a string literal, then it will treat it as such and only read until the first null terminator.
Instead you can pass a std::string directly as in your last example.
See under "Singular String Fields (proto3)" in https://developers.google.com/protocol-buffers/docs/reference/cpp-generated#oneof
I am trying to read specific binary data (2 Bytes) of a file and this mission works well, the problem when rewriting that (2 Bytes) again in the same place. Unfortunately, it changes the entire file data to zeros.
Look at the following two screenshots:
Data before writing:
Data after writing:
The code:
bool myClass::countChanger() {
std::ifstream sggFileObj_r(this->sggFilePath, std::ios::binary);
if (!sggFileObj_r.is_open()) {
std::cerr << strerror(errno) << std::endl;
return false;
}
// Buffer variable
unsigned short count;
// Move the file pointer to offset 4
sggFileObj_r.seekg(4);
// Reading data
sggFileObj_r.read((char*)&count, sizeof(unsigned short));
sggFileObj_r.close();
//// ---------------------- ////
std::ofstream sggFileObj_w(this->sggFilePath, std::ios::binary | std::ios::app);
// Increase the buffer variable by one
count += 1;
// Move the file pointer again to offset 4
sggFileObj_w.seekp(4);
// Rewriting data again to the file after modification
sggFileObj_w.write((char*)&count, sizeof(unsigned short));
sggFileObj_w.close();
return true;
}
Why did that happen and how to resolve?
UPDATE:
I have appended std::ios::app to file mode, and zeros problem solved but the specific data that I want to update is not updated.
Using
std::ofstream sggFileObj_w(this->sggFilePath, std::ios::binary)
will wipe out the data in the file since that is what ofstream does by default. You can use
std::ofstream sggFileObj_w(this->sggFilePath, std::ios::binary | std::ios::app);
to stop the data from being overridden but the issue with this is that file stream starts at the end of the file and pretends like the rest of the file doesn't exist, so you can seek back to the beggining and overwrite its contents.
What you can do instead is use a fstream like
std::fstream sggFileObj_w(this->sggFilePath, std::ios::binary | std::ios::out | std::ios::in);
To open the file in binary mode from the beginning without losing any contents. Then you can seek to where you want to write into the file.
I want to copy one image file to another new file. This is my method to do this:
std::ofstream myOutpue;
std::ifstream mySource;
//int i = 0;
mySource.open(ofn.lpstrFile, std::ios::binary);
myOutpue.open("im4.jpg", std::ios::binary);
char buffer;
char bufferToSave[100];
if (mySource.is_open())
{
//client->sendFilePacket(FileStates::START_SAVE, buffer, false,i);
i++;
while (!mySource.eof())
{
mySource >> std::noskipws >> buffer;
myOutpue << buffer;
//client->sendFilePacket(FileStates::CONTINUE_SAVE, buffer, false,i);
i++;
}
}
i++;
//client->sendFilePacket(FileStates::END_SAVE, buffer, true,i);
mySource.close();
//myOutpue.close();
This method work correctly, but my problem is that i want to copy char/bit's and send it to another client. When i doing this by each char , that not work correctly so i want to make a bigger buffor(for example char t[512]) or something like that and copy them to new file.
I try to doing this like that:
std::ofstream myOutpue;
std::ifstream mySource;
mySource.open(ofn.lpstrFile, std::ios::binary);
myOutpue.open("im4.jpg", std::ios::binary);
char buffer;
char bufferToSave[100];
if (mySource.is_open())
{
//client->sendFilePacket(FileStates::START_SAVE, buffer, false,i);
i++;
while (!mySource.eof())
{
if (i == 100)
{
for (int i = 0; i < 100; i++)myOutpue << bufferToSave[i];
i = 0;
}
mySource >> std::noskipws >> buffer;
bufferToSave[i] = buffer;
//myOutpue << buffer;
//client->sendFilePacket(FileStates::CONTINUE_SAVE, buffer, false,i);
i++;
}
}
i++;
//client->sendFilePacket(FileStates::END_SAVE, buffer, true,i);
mySource.close();
myOutpue.close();
But i get image that i can't open.
So my question is how to read file to get more bits from it and that create me the same image as original.
You have an error in your original file copy algorithm in that you should never loop using eof() as the end flag.
See: Why is iostream::eof inside a loop condition considered wrong?
Copying files can be a simple as this:
std::ofstream("output.jpg", std::ios::binary) << std::ifstream("input.jpg", std::ios::binary).rdbuf();
It uses a special overload of the output operator when passing an std::istream buffer (using rdbuf()). It copies the whole stream.
When reading a whole buffer you should use std::istream::read:
std::ifstream ifs("input.jpg", std::ios::binary)
char buffer[1025]; // create a buffer
// keep going as long as the reading succeeds
while(ifs.read(buffer, sizeof(buffer)))
{
// ifs.gcount() is the number of chars read successfully
client->sendFilePacket(buffer, ifs.gcount()); // send all bytes
}
I know it's been a long time, but reading these topic I found the solution:
std::ifstream ifs(ofn.lpstrFile, std::ios::binary);
std::ofstream myOutpue;
char buffer[1024]; // create a buffer
myOutpue.open("output.jpg", std::ios::binary);
//client->sendFilePacket(FileStates::START_SAVE, buffer, false, i);
while (ifs.read(buffer, sizeof(buffer)))
{
myOutpue.write(buffer, ifs.gcount());
}
//
myOutpue.write(buffer, ifs.gcount());
myOutpue.close();
Note: My answer is similar to #dawcza94, but to avoid black screen, after the loop you have to save the rest of the reading, because in the loop you save only what fits in the buffer, and the rest you ignore. Sometimes it happens that the rest can be a few characters long, and it looks like the images are the same size, but they aren't.
Note2: I posted here to help those who are still in trouble as I was!!
C++ FAQ:
You probably want to use iostream’s read() and write() methods instead of its >> and << operators. read() and write() are better for binary mode; >> and << are better for text mode.
You can specify how much you want to read. With gcount you can ask, how much characters are read successfully. Same goes for write.
I try with this code:
std::ifstream ifs(ofn.lpstrFile, std::ios::binary);
std::ofstream myOutpue;
char buffer[1024]; // create a buffer
myOutpue.open("output.jpg", std::ios::binary);
//client->sendFilePacket(FileStates::START_SAVE, buffer, false, i);
while (ifs.read(buffer, sizeof(buffer)))
{
//client->sendFilePacket(FileStates::CONTINUE_SAVE, buffer, false, ifs.gcount());
myOutpue.write(buffer, ifs.gcount());
}
//client->sendFilePacket(FileStates::END_SAVE, buffer, true, i);
myOutpue.close();
But when i doing this like that, in my copy of image i got only half of original image and half of black screen( number of kb is the same like in original file), so i don't know what's a problem with that ?
Instead of using "manual" copy, try using ifstream::read method
I'm new to C++, I have an image named "test.jpg", i convert it to base64 and decode it again like this:
std::ifstream inputFile;
inputFile.open("test.jpg",std::ios::binary);
std::filebuf* pbuf = inputFile.rdbuf();
inputFile.seekg (0, ios::end);
int length = inputFile.tellg();
// allocate memory to contain file data
char* buffer=new char[length];
// get file data
pbuf->sgetn (buffer,length);
inputFile.close();
CBase64 base64;
string encodedData = base64.base64_encode((unsigned char*)buffer,length);
delete[] buffer;
string decodedData = base64.base64_decode(encodedData);
ofstream outPutFile;
outPutFile.open("test2.jpg",ios::binary | ios::out);
outPutFile.write(decodedData.c_str(), decodedData.length());
outPutFile.close();
the "test2.jpg" has exact same size as "test.jpg"(the original file) but, i can't open it.
i couldn't find what is the problem.
i got it working. i just replaced:
outPutFile.open("test2.jpg",ios::binary | ios::out);
with
outPutFile.open("test2.jpg", ios_base::out | ios_base::binary);
std::string path = "file.txt";
std::string cfgString = "data";
std::ofstream output(path.c_str(), ios_base::out | std::ios::binary);
if (output.is_open()) {
output.write(cfgString.data(), cfgString.length());
}
output.close();
Apparently, there is no superficial problem with your file writing logic even though there are some irregularities. The biggest problem is in your main logic.
The program seems to be simple program of copying a file. What you are doing is reading a file, converting its data to base64 string and then again decoding the data to std::string. Now one small problem. Conversion of base64 string cannot be successfully done into a null terminated ANSI string for obvious reasons that any 0 in decoded binary data will terminate the string prematurely. Secondly you are opening a file in binary mode to write but trying to write std::string in the file. But that doesn't matter as you data has already been corrupted in your previous operation.
To solve this, you can simply use file copying example as this or make sure you write only binary data with care to your output file which means read in binary from input file and write to output file the same buffer. No base64 encoding decoding is required.
it looks like you forgot to write
inputFile.seekg (0, ios::beg);
after getting file length. it means you try to read from the end of the file instead of its beginning.
The reader and writer
#include<string>
#include<fstream>
#include<memory>
class BinarySearchFile{
BinarySearchFile::BinarySearchFile(std::string file_name){
// concatenate extension to fileName
file_name += ".dat";
// form complete table data filename
data_file_name = file_name;
// create or reopen table data file for reading and writing
binary_search_file.open(data_file_name, std::ios::binary); // create file
if(!binary_search_file.is_open()){
binary_search_file.clear();
binary_search_file.open(data_file_name, std::ios::out | std::ios::binary);
binary_search_file.close();
binary_search_file.open(data_file_name), std::ios::out | std::ios::in | std::ios::binary | std::ios::ate;
}
std::fstream binary_search_file;
void BinarySearchFile::writeT(std::string attribute){
if(binary_search_file){
binary_search_file.write(reinterpret_cast<char *>(&attribute), attribute.length() * 2);
}
}
std::string BinarySearchFile::readT(long filePointerLocation, long sizeOfData)
{
if(binary_search_file){
std::string data;
data.resize(sizeOfData);
binary_search_file.seekp(filePointerLocation);
binary_search_file.seekg(filePointerLocation);
binary_search_file.read(&data[0], sizeOfData);
return data;
}
};
The reader call
while (true){
std::unique_ptr<BinarySearchFile> data_file(new BinarySearchFile("classroom.dat"));
std::string attribute_value = data_file->read_data(0, 20);
}
The writer call
data_file->write_data("packard ");
The writer writes a total of 50 bytes
"packard 101 500 "
The reader is to read the first 20 bytes and the result is "X packard X" where X represents some malformed bytes of data. Why is the data read back in x-number of bytes corrupt?
You can't simply write data out by casting it's address to a char* and hoping to get anything useful. You have to define the binary format you want to use, and implement it. In the case of std::string, this may mean outputing the length in some format, then the actual data. Or in the case where fixed length fields are needed, forcing the string (or a copy of the string) to that length using std::string::resize, then outputting that, using std::string::data() to get your char const*.
Reading will, of course, be similar. You'll read the data into a std::vector<char> (or for fixed length fields, a char[]), and parse it.
binary_search_file.write(reinterpret_cast<char *>(&attribute), attribute.length() * 2);
It is incorrect to cast std::string to char* if you need char* you must use attribute.c_str().
std::string apart from string pointer contains other data members, for example, allocator, your code will write all of that data to file. Also I don't see any reason to multiply string length by 2. +1 makes sense if you want to output terminating zero.