C++ Writing to file vector of byte - c++

I have:
typedef unsigned char;
std::vector<byte> data;
I tried to save data in file this way (but I have error):
fstream file(filename,ios::out);
file.write(&data, data.size());
How to process or cast data to write it in file.

To store a vector in a file, you have to write the contents of the vector, not the vector itself. You can access the raw data with &vector[0], address of the first element (given it contains at least one element).
ofstream outfile(filename, ios::out | ios::binary);
outfile.write(&data[0], data.size());
This should be fairly efficient at writing. fstream is generic, use ofstream if you are going to write.

*Statement file.write(&buffer[0],buffer.size()) makes error:
error C2664: 'std::basic_ostream<_Elem,_Traits>::write' : cannot
convert parameter 1 from 'unsigned char *' to 'const char *'
*In my compiler (VS2008) I don't have data() method for vector.
I think below is correct:
file.write((const char*)&buffer[0],buffer.size());

Use vector::data to get a pointer the the underlying data:
file.write(data.data(), data.size());

You are to pass the address of the first element, not the address of the vector object itself.
&data[0]
Note: Make sure that the vector is not empty before doing this.

A lot of these solutions are only partially complete (lacking includes & casts), so let me post a full working example:
#include <vector>
#include <fstream>
int main()
{
std::vector<std::byte> dataVector(10, std::byte{ 'Z' });
const std::string filename = "C:\\test_file.txt";
std::ofstream outfile(filename, std::ios::out | std::ios::binary);
outfile.write(reinterpret_cast<const char*>(dataVector.data()), dataVector.size());
return 0;
}

I think that ostream.write (myVector[0], ...) will not work, as it does not work in reading into vector (that I had.)
What works is ostream.write(MyVector.data(), ...)
Note: for reading use ifstream.read(const_cast(MyVector.data()), ...)

Related

Unable to read a binary file into std::vector<std::byte> in C++

I am trying to read a .WAV file in C++ into a vector of binary data:
typedef std::istreambuf_iterator<char> file_iterator;
std::ifstream file(path, std::ios::in | std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("Failed to open " + path);
}
std::vector<std::byte> content((file_iterator(file)), file_iterator());
When I attempt to compile this code I get an error:
Cannot convert 'char' to 'std::byte' in initialization
However if I change the vector to std::vector<unsigned char> it works fine.
Looking at the documentation for std::byte it looks like it is supposed to act like an unsigned char so I'm not sure where the compiler is getting confused.
Is there any particular way you are supposed to go about reading a file into a vector of bytes? (I am looking for a modern C++ approach)
I am using MinGW 7.3.0 as my compiler.
EDIT:
This question is not a duplicate because I am specifically concerned about modern C++ techniques and the use of std::byte which is not discussed in that question.
std::byte is a scoped enum. As such, there are restrictions on conversion to the type that do not exist for fundamental types like char.
Because the underlying type of std::byte is unsigned char, you can't convert a (signed) char to a byte during initialization because the conversion is a narrowing conversion.
One solution is to use a vector of unsigned char to store the file content. Since a byte is not an arithmetic type, many of the numeric operations do not exist for byte (only the bitwise ones).
If you must use std::byte, define the iterator and fstream using that type:
typedef std::istreambuf_iterator<std::byte> file_iterator;
std::basic_ifstream<std::byte> file(path, std::ios::in | std::ios::binary);

ifstream binary read/write only takes char*?

I'm having a little trouble figuring out how to write this value to a file correctly. I did a little research on the internet and found this article.
http://www.eecs.umich.edu/courses/eecs380/HANDOUTS/cppBinaryFileIO-2.html
#include <fstream>
#include <iostream>
int main()
{
int testVar = 71;
std::ofstream outputFile;
outputFile.open("C:/binary.dat", std::ios::out | std::ios::binary);
outputFile.seekg(0);
outputFile.write(&testVar, sizeof(testVar));
outputFile.close();
}
What I understand from the article is that the first parameter is a void pointer? which means that it will accept any type? But when I'm typing it out, the intelisense says there is no overload and the first parameter takes type char*.
Am I using the wrong header or something from an older C++ version??
Could really use some help here.
Thanks!
I am not familiar with the history of development of the functions. Hence, I can't comment on why the arguments are of type char* and not void*.
To solve your problem...
You can use:
outputFile.write(reinterpret_cast<char*>(&testVar), sizeof(testVar));
Use reinterpret_cast while using istream::read() also.
You really just need to cast it to char* like was said before, but there are other problems with the code.
seekg() is for input streams, and you are writing an output file. If you meant to clear the line, just open the file with trunc.
#include <fstream>
#include <iostream>
int main()
{
int testVar = 71;
std::ofstream outputFile("C:/binary.dat", std::ios::out | std::ios::binary | std::ios::trunc);
outputFile.write((char*)&testVar, sizeof(testVar));
outputFile.close();
}
The first parameter is a char*.
char having size 1 byte signifies byte by byte data and char* signifies a block of data.
Hence when writing raw binary data, the data is passed as a char* and size of the block.
Am I using the wrong header or something from an older C++ version??
No it is not the problem as stated it is char* and not void* that is taken as argument.
NOTE The seekg() member function is for input streams. I think the functionality you needed was seekp()

What is the most efficient way to make data going to a ostream to go to a array of bytes?

I have a function that I need to call which takes an ostream.
What I want is the data in a char array, a char*.
So what I do is the following to copy the ostream data into imageBytes. The problem is, this seems inefficient to me, I just want to get data into a char[ONE_MEGABYTES]. Outputting it to the stringstream seems to be a very indirect way to do it. How could I do it more efficiently?
#define ONE_MEGABYTE 1048576
volatile char* imageBytes = new char[ONE_MEGABYTE];
stringstream pngImageStringStream(ios_base::in | ios_base::out | ios_base::binary);
image.write_stream(pngImageStringStream);
imageLength = pngImageStringStream.tellp();
memcpy( (void*)imageBytes, (void*)pngImageStringStream.str().c_str(), imageLength);
The most efficient way would be to use read:
pngImageStringStream.read(imageBytes, imageLength);

How can I use fread() on a binary file to read the data into a std::vector?

Follow-up question on an earlier question I had, that has been perfectly answered. To quickly recap, I had trouble creating a class holding a huge array (stack overflow error). In the answers, some users recommended I use std::vector instead.
The function to read in the data looks like this:
Test()
{
memset(myarray, 0, sizeof(myarray));
FILE* fstr = fopen("myfile.dat", "rb");
size_t success= fread(myarray, sizeof(myarray), 1, fstr);
fclose(fstr);
}
for a myarray which looked like this:
int myarray[45000000];
My question is: How can I read this into a preferable:
std::vector<int> myvector;
I searched google , and have found multiple answers, usually pointing to the following code:
std::ifstream input("myfile.dat", std::ios::in | std::ifstream::binary);
std::copy(std::istream_iterator<int>(input),
std::istream_iterator<int>(),
std::back_inserter(myvector));
After implementing this, and when calling myvector.size() I get 16 (for whatever reason), and accessing a vector element leads to an immediate crash for going out of the vector bounds.
So what do I have to do to get this right? I once read somewhere that I could just simply use the "old" method, and then reading the array into the vector, but this seems to defeat the purpose of using the vector in the first place.
fread() reads your file binary, while ifstream_iterator tries to extract formatted ints (like 42).
You want to resize your vector and use input.read(...) instead:
const size_t size = 45000000; // change this to the appropriate value
std::vector<char> myvector(size, 0);
std::ifstream input("myfile.dat", std::ios::in | std::ifstream::binary);
input.read(&myvector[0], myvector.size());
Note that you need to use a std::vector<char> since read expects the first parameter to be a char *. You can use other types T if you cast the type correctly:
input.read(reinterpret_cast<char*>(&myvector[0]), myvector.size() * sizeof(T));
If you're using C++ you should try to avoid using the C FILE APIs all together -- so you're on the right track. The problem you're having is that istream_iterator reads input as text, not binary -- it's expecting ASCII digits. This this out instead:
std::vector<int> vec(45000000);
std::filebuf fb;
fb.open("myfile.dat", std::ios_base::in | std::ios_base::binary);
fb.sgetn((char*)&vec[0], vec.size() * sizeof(vec[0]));

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.