struct st {
char *p;
int len;
};
this is the structure which i need to write to a binary file, along with the string which is saved in
char *p
I am supposed to write a binary file with the char array data. After writing to binary file. Is should also be able to read it in the same structure from binary file.
I tried using FSEEK_END to get the binary size, and doing fread according to file size, but it is not saving string. Please suggest. Any help/suggestion/pointer would be appreciated.
thanks in advance.
sample code:
struct st {
char *p;
int len;
};
struct st varr;
varr.len = 100;
varr.p = new char[gen];
strcpy(varr.p, "Hello World");
FILE *p;
p=fopen("address","wb");
fwrite(&varr,sizeof(struct st),1,p);
fclose(p);
this will write me the pointer to the binary file. But i want to write here whole string, but the point is, it should done with one fwrite.
There is no use writing this to a binary file. A text file would suffice.
However, if this is your homework assignment (or so), I suggest you proceed as follows:
write the length as an integer;
write len bytes of the string. This does not include a terminating null.
When reading back:
read the integer length;
allocate memory of this length plus one byte
read the string into that memory and add the terminating null.
Fill your structure with this length and the pointer to allocated memory.
In your comments you keep iterating you want to read and write in one step. With your current data structure that is not possible because the character string wil always be somewhere else in memory and fwrite can only write a contiguous block of memory.
However, would you change your data structure to:
struct st{
char p[128];
int len;
};
then you can write and read in one go because now the struct is a contiguous memory block. But now the string is limited to this 128 bytes(or any size you make it).
I would suggest saving the strlen(p) first, followed by all the chars pointed by char *p. If you try to fwrite the struct as it is, you'll end up (in the file) with the value for the address with regrads to the first char. You do not want to save the address of the 1st char, do you?
If you are so much concerned about not having two fwrite you can have wrapper structure with zero length array.
struct st_wrap {
int len;
char p[0];
};
struct st_wrap *temp= malloc (sizeof (struct st_wrap ) + strlen(varr.p)+1);
temp->len = strlen(varr.p);
//Copy the data
strcpy(temp->p,varr.p);
//Then write it to file
fwrite(temp,sizeof(struct st_wrap)+strlen(varr.p),1,p);
Assuming you have written the length and string to binary file:
FILE* fp = fopen("file.bin", "wb");
/* writing */
st str = { .p = "foo", .len = 3 };
fwrite((char*)(&str.len), sizeof(str.len), sizeof(str.len), fp);
fwrite(str.p, str.len, str.len, fp);
You can now (in a different context) read the content from file into the struct object:
FILE* fp = fopen("file.bin", "rb");
st str;
/* read length */
fread((char*)&str.len, sizeof(str.len), sizeof(str.len), fp);
/* allocate enough space */
str.p = malloc((str.len + 1) * sizeof(char));
/* read string */
fread(str.p, str.len + 1, str.len + 1, fp);
Remember to free memory when you're done with p:
free(str.p);
I tried lot of ways, but #kiran Biradar's answer helped me a lot. hence displaying the working code here.
WriteBinaryFile.cpp
struct st_wrap {
int len;
int crc;
char p[0];
};
int main ()
{
char p[100] = "Hello World";
struct st_wrap *temp= (struct st_wrap*) malloc (sizeof (struct st_wrap ) + strlen(p)+1);
temp->len = strlen(p);
temp->crc = 400;
strcpy(temp->p,p);
cout << temp->p << endl;
cout << temp->len << endl;
cout << temp->crc << endl;
FILE *p1;
p1=fopen("binary.dat","wb");
fwrite(temp,sizeof(struct st_wrap)+strlen(p),1,p1);
fclose(p1);
}
ReadBinaryFile.cpp
struct st_wrap {
int len;
int crc;
char p[0];
};
int main ()
{
struct st_wrap *vw;
FILE *f=fopen("binary.dat","rb");
fseek(f, 0, SEEK_END);
long int filesize = ftell(f);
fseek(f, 0, SEEK_SET);
vw = (struct st_wrap*) malloc (filesize);
fread(vw,filesize,1,f);
cout << vw->len << endl;
cout << vw->p << endl;
cout << vw->crc << endl;
return 0;
}
Related
I am trying to read a string from a binary file but cant seem to get it to work. I am a pretty new to c++. Can anybody help please? Thanks.
string Name = "Shaun";
unsigned short int StringLength = 0;
int main()
{
StringLength = Name.size();
ofstream oFile("File.txt", ios::binary|ios::out);
oFile.write((char*)&StringLength, sizeof(unsigned short int));
oFile.write(Name.c_str(), StringLength);
oFile.close();
StringLength = 0;
Name = "NoName";
ifstream iFile("File.txt", ios::binary|ios::in);
if(!iFile.is_open())
cout << "Failed" << endl;
else
{
iFile.read((char *)&StringLength, sizeof(unsigned short int));
iFile.read((char *)&Name, StringLength);
}
cout << StringLength << " " << Name << endl;
system("Pause>NUL");
return 0;
}
This is the problematic line.
iFile.read((char *)&Name, StringLength);
You are reading the char* part of a std::string directly into the memory of Name.
You need to save both the size of the string as well as the string so that when you read the data, you would know how much memory you need to read the data.
Instead of
oFile.write(Name.c_str(), StringLength);
You would need:
size_t len = Name.size();
oFile.write(&len, sizeof(size_t));
oFile.write(Name.c_str(), len);
On the way back, you would need:
iFile.read(&len, sizeof(size_t));
char* temp = new char[len+1];
iFile.read(temp, len);
temp[len] = '\0';
Name = temp;
delete [] temp;
you need to create a buffer of char type.
char *buffer = new char[size];
Then use your buffer as the parameter to read function
iFile.read(buffer, size);
Instead of this
iFile.read((char *)&Name, StringLength);
Try this:
Name.resize(StringLength);
iFile.read((char *)&Name[0], StringLength);
Your original line overwrites the string object data from the beginning, which may contain the string length and capacity, for instance, instead of the character data. Also you don't resize the string appropriately to be able to contain the data.
Conceptually, you need to understand the data stream which you are reading. Not everything use ASCII which uses a byte size of 8 bits. I also notice you did not set bit size read which can be as little as one bit to as large as {R, B, G, A} color set. Using your basic 2 dimensional reiteration structured code.
I want to find a specific string "fileSize" in a binary file.
The purpose of finding that string is to get 4 bytes that next to the string because that 4 bytes contains the size of data that I want to read it.
The content of the binary file like the following:
The same string in another position:
Another position:
The following is the function that writes the data to a file:
void W_Data(char *readableFile, char *writableFile) {
ifstream RFile(readableFile, ios::binary);
ofstream WFile(writableFile, ios::binary | ios::app);
RFile.seekg(0, ios::end);
unsigned long size = (unsigned long)RFile.tellg();
RFile.seekg(0, ios::beg);
unsigned int bufferSize = 1024;
char *contentsBuffer = new char[bufferSize];
WFile.write("fileSize:", 9);
WFile.write((char*)&size, sizeof(unsigned long));
while (!RFile.eof()) {
RFile.read(contentsBuffer, bufferSize);
WFile.write(contentsBuffer, bufferSize);
}
RFile.close();
WFile.close();
delete contentsBuffer;
contentsBuffer = NULL;
}
Also, the function that searches for the string:
void R_Data(char *readableFile) {
ifstream RFile(readableFile, ios::binary);
const unsigned int bufferSize = 9;
char fileSize[bufferSize];
while (RFile.read(fileSize, bufferSize)) {
if (strcmp(fileSize, "fileSize:") == 0) {
cout << "Exists" << endl;
}
}
RFile.close();
}
How to find a specific string in a binary file?
I think of using find() is an easy way to search for patterns.
void R_Data(const std::string filename, const std::string pattern) {
std::ifstream(filename, std::ios::binary);
char buffer[1024];
while (file.read(buffer, 1024)) {
std::string temp(buffer, 1024);
std::size_t pos = 0, old = 0;
while (pos != std::string::npos) {
pos = temp.find(pattern, old);
old = pos + pattern.length();
if ( pos != std::string::npos )
std::cout << "Exists" << std::endl;
}
file.seekg(pattern.length()-1, std::ios::cur);
}
}
How to find a specific string in a binary file?
If you don't know the location of the string in the file, I suggest the following:
Find the size of the file.
Allocate memory for being able to read everything in the file.
Read everything from the file to the memory allocated.
Iterate over the contents of the file and use std::strcmp/std::strncmp to find the string.
Deallocate the memory once you are done using it.
There are couple of problems with using
const unsigned int bufferSize = 9;
char fileSize[bufferSize];
while (RFile.read(fileSize, bufferSize)) {
if (strcmp(fileSize, "filesize:") == 0) {
cout << "Exists" << endl;
}
}
Problem 1
The strcmp line will lead to undefined behavior when fileSize actually contains the string "fileSize:" since the variable has enough space only for 9 character. It needs an additional element to hold the terminating null character. You could use
const unsigned int bufferSize = 9;
char fileSize[bufferSize+1] = {0};
while (RFile.read(fileSize, bufferSize)) {
if (strcmp(fileSize, "filesize:") == 0) {
cout << "Exists" << endl;
}
}
to take care of that problem.
Problem 2
You are reading the contents of the file in blocks of 9.
First call to RFile.read reads the first block of 9 characters.
Second call to RFile.read reads the second block of 9 characters.
Third call to RFile.read reads the third block of 9 characters. etc.
Hence, unless the string "fileSize:" is at the boundary of one such blocks, the test
if (strcmp(fileSize, "filesize:") == 0)
will never pass.
I'm using memcpy to copy a specific number of chars from a char array to a char *. But when I read the char * have always trash in the end.
I'm using libssh2 lib to send commands to my raspberry pi and receive the output.
libssh2_channel_read will return the number of chars of the output int x and the output text will be on the char buffer[32].
Code I'm using:
char buffer[32];
int x = libssh2_channel_read(channel, buffer, sizeof(buffer));
char * output = (char *)malloc(sizeof(char)*x);
memcpy(output, buffer, x-2); // x - 2 because of "\r\n"
libssh2_channel_free(channel);
channel = NULL;
cout << output << endl;
Example of output:
0══²²²²
I only want the 0
Welcome to C++.
You are copying the values you care about but not the terminating '\0' character. Assuming x is valid (that is: x > 3 and x <= sizeof(buffer)) you can say:
output[x - 2] = '\0';
after the call to memcpy() and you should get what you expect.
However: when you're dealing with communications and buffers like this you need to be careful and check everything.
I think you shouldn't be using raw arrays and memcpy and the like here.
You are probably better off with the containers from the C++ standard library:
http://en.cppreference.com/w/cpp/container/vector
http://en.cppreference.com/w/cpp/string/basic_string
Directly write into char* buffer of std::string
Example:
std::vector<char> buffer(32);
int x = libssh2_channel_read(channel, &buffer[0], buffer.size());
// probably x will be smaller or equal the current size of the buffer
buffer.resize(x);
// if it's a string, why not have it as a std::string
std::string data(buffer.begin(), buffer.end());
std::cout << data << '\n';
Use std::string:
char buffer[32];
int x = libssh2_channel_read(channel, buffer, sizeof(buffer));
std::string output{ buffer, buffer + x - 2 };
libssh2_channel_free(channel);
channel = NULL;
cout << output << endl;
I'm facing some troubles with code for my school. The problem is that I have a char pointer named "contenido" like this:
class Archivo {
public:
Archivo(const char *filename, int banderas);
Archivo(const char *filename, int banderas, mode_t modo);
~Archivo();
size_t lee(size_t nbytes);
size_t escribe(const void *buffer, size_t nbytes);
char *get_contenido();
void cerrar();
protected:
string nombreArchivo;
int fd;
//Problematic char pointer
char *contenido;
};
The code is supposed to be a class implementation of the c functions read(), write() and open(), the specific method where i need to set a new size for my char pointer is size_t lee(size_t nbytes); that i implemented like this:
size_t Archivo::lee(size_t nbytes) {
contenido = new char[nbytes];
cout << "contenido: " << sizeof(contenido) << endl;
nbytes = read(fd, contenido, sizeof(contenido));
return nbytes;
}
In this method, I should receive a new size for "contenido" that in this case, it works as my buffer for the read() function but unfortunately, it doesn't matter which value I use it always returns "8" in the call of sizeof(contenido). I have tried using malloc() in the constructor, using malloc() and then realloc() and as you may see using new char[size_t] but in every single case I receive the same result of "8". I researched about this and in most cases the suggestion was to use std::vector but in this specific case it's necessary to use char * due to the request of my professor. I hope you can help me and thanks in advance.
sizeof(contenido) evaluates to the size of a pointer, which is 8 on your platform.
To restore the contents of a string, you need to use the length of the string as argument to read.
size_t Archivo::lee(size_t nbytes) {
contenido = new char[nbytes];
cout << "contenido: " << sizeof(contenido) << endl;
nbytes = read(fd, contenido, nbytes);
return nbytes;
}
If contenido is supposed to be a null terminated string, you probably need to allocate one more byte for it and make sure to add the terminating null character.
size_t Archivo::lee(size_t nbytes) {
contenido = new char[nbytes+1];
cout << "contenido: " << sizeof(contenido) << endl;
nbytes = read(fd, contenido, nbytpes);
contenido[nbytes] = '\0';
return nbytes;
}
You are calling the sizeof() operator on a pointer, therefore you get the size of the pointer and not the size of content it's pointing to. Pointer in memory is usually like another regular variable (int for example) except it contains an address instead of an actual value. You always get the number 8 probably because your machine is 64 bit, so a pointer should be able to hold any address in the 64 bit space. (8 bytes = 64 bit)
If you want to get the size in bytes of the content (the memory you allocated), you should take the size of char (the type the content is made of) using sizeof() and then multiply that by the number of chars allocated, like so:
sizeof(char)*nbytes
I am trying to read a string from a binary file but cant seem to get it to work. I am a pretty new to c++. Can anybody help please? Thanks.
string Name = "Shaun";
unsigned short int StringLength = 0;
int main()
{
StringLength = Name.size();
ofstream oFile("File.txt", ios::binary|ios::out);
oFile.write((char*)&StringLength, sizeof(unsigned short int));
oFile.write(Name.c_str(), StringLength);
oFile.close();
StringLength = 0;
Name = "NoName";
ifstream iFile("File.txt", ios::binary|ios::in);
if(!iFile.is_open())
cout << "Failed" << endl;
else
{
iFile.read((char *)&StringLength, sizeof(unsigned short int));
iFile.read((char *)&Name, StringLength);
}
cout << StringLength << " " << Name << endl;
system("Pause>NUL");
return 0;
}
This is the problematic line.
iFile.read((char *)&Name, StringLength);
You are reading the char* part of a std::string directly into the memory of Name.
You need to save both the size of the string as well as the string so that when you read the data, you would know how much memory you need to read the data.
Instead of
oFile.write(Name.c_str(), StringLength);
You would need:
size_t len = Name.size();
oFile.write(&len, sizeof(size_t));
oFile.write(Name.c_str(), len);
On the way back, you would need:
iFile.read(&len, sizeof(size_t));
char* temp = new char[len+1];
iFile.read(temp, len);
temp[len] = '\0';
Name = temp;
delete [] temp;
you need to create a buffer of char type.
char *buffer = new char[size];
Then use your buffer as the parameter to read function
iFile.read(buffer, size);
Instead of this
iFile.read((char *)&Name, StringLength);
Try this:
Name.resize(StringLength);
iFile.read((char *)&Name[0], StringLength);
Your original line overwrites the string object data from the beginning, which may contain the string length and capacity, for instance, instead of the character data. Also you don't resize the string appropriately to be able to contain the data.
Conceptually, you need to understand the data stream which you are reading. Not everything use ASCII which uses a byte size of 8 bits. I also notice you did not set bit size read which can be as little as one bit to as large as {R, B, G, A} color set. Using your basic 2 dimensional reiteration structured code.