Parsing binary files in C++ - c++

I'm trying to read a binary format using C++
For some reason I'm able to parse only the first variable.
The header sequence is:
[2 byte integer][1 byte integer][1byte integer]
#include <iostream>
#include <fstream>
using namespace std;
struct HDR {
unsigned short int signature;
unsigned char version;
unsigned char tricnt;
} header;
int main(){
ifstream infile("1.mdl",ios::in | ios::binary);
if(!infile){
cout<<"Error\n";
return 1;
}
infile.read(reinterpret_cast<char *>(&header),sizeof(HDR));
cout<<"SIG "<<header.signature<<endl;
cout<<"VER "<<header.version<<endl;
cout<<"TRI "<<header.tricnt<<endl;
return 0;
}
For some reason I'm able to parse only the signature, the rest of the structure is empty.

Unless you have specific knowledge of the padding used by your implementation you should read into the members individually.
infile.read(reinterpret_cast<char *>(&header.signature), sizeof header.signature);
infile.read(reinterpret_cast<char *>(&header.version), sizeof header.version);
infile.read(reinterpret_cast<char *>(&header.tricnt), sizeof header.tricnt);
Of course, you are still relying on unsigned short being 2 bytes on your platform and the representation in the file having the same endianness as your machine but at least you aren't making assumptions about structure padding.
Naturally, when you're printing unsigned char the character represented will be printed. If you want to see the numeric value you should cast to a non-char integer type. ASCII 1 (start of header) and 3 (end of text) are control characters and not usually visible when printed.
cout<<"VER "<< static_cast<int>(header.version) <<endl;
cout<<"TRI "<< static_cast<int>(header.tricnt) <<endl;

Related

Why is reading uint8_t as hex not working as expected?

Consider code like this:
#include <iostream>
#include <sstream>
int main()
{
std::stringstream ss;
ss << "a0 b1";
uint8_t byte;
ss >> std::hex >> byte;
std::cout << std::hex << byte << std::endl;
return 0;
}
Why does this output a instead of a0 even though a0 fits in uint8_t as hex?
Because uint8_t is also (probably) unsigned char, for which special rules exist when you perform formatted extraction from a C++ stream.
Unfortunately this is just an alias, not a distinct type.
Basically it's skipping the "lexically convert to a number" step because it thinks you want to pull out a character. The character 'a'.
I think you'll want to read into an unsigned int then downsize if needed.
If you do downsize to a uint8_t, you're also going then to have to promote it back to a larger int (lol) for much the same reason, to trigger serialisation.
(live demo)
To be honest I'd just avoid the small fixed-width types when dealing with streams (unless you're doing unformatted work with read() and write()). It's too easy to forget about this problem.

How can I store the integer in a character array?

#include <iostream>
using namespace std;
int main() {
char ch[19];
int c;
cin >>c;
ch[0]=c;
ch[1]='\0';
cout << ch;
}
what I want is to store the intger c at the location ch[0] but it stores the ascii value corresponding to that number like if.I put 97 then it stores a.
How can I put the character 9 in the character array ch?
Even if I typecast the variable c to char it doesn't help.
Standard streaming operators are overloaded on the type of their operands. By default, streaming into an int will read decimal digits, streaming into a char will read one character, streaming into a std::string will read a word, and so on.
So, if you want to read a single character (the digit 9), just stream into a char, which you already have at hand:
std::cin >> ch[0];

How to write a file byte by byte using c++

How to write a file byte by byte using c++?
unsigned short array[2]={ox20ac,0x20bc};
if i have a hexadecimal value 0x20ac how can i write it byte by byte in a file using c++
You can try something like this:
#include <fstream>
...
ofstream fout;
fout.open("file.bin", ios::binary | ios::out);
int a[4] = {100023, 23, 42, 13};
fout.write((char*) &a, sizeof(a));
fout.close();
One option, using standard C++ library:
#include <fstream>
#include <assert.h>
void main()
{
unsigned short array[2]={ox20ac,0x20bc};
std::ofstream file;
file.open("C:/1.dat", std::ios_base::binary);
assert(file.is_open());
for(int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
file.write((char*)(array + i * sizeof(array[0])), sizeof(array[0]));
file.close();
}
Alternatively, you can easily write your whole data in one go (without a loop):
file.write((const char*)array, sizeof(array));
To open an output file, use ofstream (output file stream, a subclass of ostream). Use the ios_base::binary mode (as second argument in the constructor or the open() member function) if you're not sure whether your output is human-readable text (ASCII).
To write a single byte, use the ostream member function "put". To write more than one byte at a time, use the ostream member function "write".
There are ways of taking data types (int, for example) longer than one byte and using them as arrays of bytes. This is sometimes called type-punning and is described in other answers, but beware of endianness and different sizes of data types (int can be 2-8 bytes), which can be different on different machines and compilers.
To test your output, reopen it as an input file and print the bytes.
ifstream in("myfile.txt", ios_base::binary);
while(!in.eof()) printf("%02X ", in.get()); //print next byte as a zero-padded width-2 capitalized hexadecimal).
in.close();
Or just use a hex editor like normal people.
you can use write function or ostream .
Use c++ function is ostream.

Reading bytes one by one from binary file

this is my question, i want to open a .jpg file and write each byte as a decimal number (0-255) separated with a comma, into another .txt file. now it should be able to build the .jpf file again using that txt file. this is how i tried to do it.
#include<iostream>
#include<fstream>
using namespace std;
int main()
{
long x;
char *s;
ifstream ifs("image.jpg",ios::binary);
ifs.seekg(0,ios::end);
x=ifs.tellg();
ifs.seekg(0,ios::beg);
s=new char[x];
ifs.read(s,x);
ifs.close();
ofstream is("image.txt");
for(int i=0;i<x;i++){
is<<(unsigned int)s[i]<<",";
}
now this program creats image.txt with decimal numbers as follows,
4294967295,4294967256,4294967295,4294967264,0,16,74,70,73,70,0,1,......
here some numbers seems to be 4bytes long, s[i] refers only one byte, so how can (int)s[i] return a large number than 255. please can some one help me on this.... thanks..
It seems on your machine char is signed. So when you cast a negative number to unsigned int, you get a big value. The big values in the output are negative values when representing them using char. Note that when char is signed, its value can be -128 to 127 but a byte can be between 0 to 255. So any value greater than 127 would become negative between the range -128 to -1.
Use unsigned char as:
unsigned char *s;
Or do this:
is<< static_cast<unsigned int> (static_cast<unsigned char>(s[i]) )<<",";
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
casting to unsigned char first
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
then casting to unsigned int
That is, cast first char to unsigned char, then to unsigned int.
Well that is all about the issue you're facing. Now some notes on style and idioms. In C++, you should avoid using new as much as possible. In your case, you can use std::vector as:
//define file stream object, and open the file
std::ifstream file("image.jpg",ios::binary);
//prepare iterator pairs to iterate the file content!
std::istream_iterator<unsigned char> begin(file), end;
//reading the file content using the iterator!
std::vector<unsigned char> buffer(begin,end);
The last line reads all the data from the file into buffer. Now you can print them as:
std::copy(buffer.begin(),
buffer.end(),
std::ostream_iterator<unsigned int>(std::cout, ","));
For all these to work, you need to include the following headers in addition to what you have already added in your code:
#include <vector> //for vector
#include <iterator> //for std::istream_iterator and std::ostream_iterator
#include <algorithm> //for std::copy
As you can see, this idiomatic solution doesn't use pointer and new, neither does it use cast!

C++ and binary files - newbie question

I have the following code and i am trying to write some data in a binary file.
The problem is that i don't have any experience with binary files and i cant understand what i am doing.
#include <iostream>
#include <fstream>
#include <string>
#define RPF 5
using namespace std;
int write_header(int h_len, ofstream& f)
{
int h;
for(h=0;h<h_len;h++)
{
int num = 0;
f.write((char*)&num,sizeof(char));
}
return 0;
}
int new_file(const char* name)
{
ofstream n_file(name,ofstream::binary);
write_header(RPF,n_file);
n_file.close();
return 0;
}
int main(int argc, char **argv)
{
ofstream file("file.dat",ofstream::binary);
file.seekp(10);
file.write("this is a message",3);
new_file("file1.dat");
cin.get();
return 0;
}
1. As you can see i am opening file.dat and writing inside the word "thi". Then i open the file and i see the ASCII value of it. Why does this happen?
Then i make a new file file1.dat and i try to write in it the number 0 five times.
What i am supposed to use?
this
f.write((char*)&num,sizeof(char));
or this
f.write((char*)&num,sizeof(int));
and why i cant write the value of the number as is and i have to cast it as a char*?
Is this because write() works like this or i am able to write only chars to a binary file?
Can anyone help me understand what's happening?
Function write() that a pointer to your data buffer and the length in bytes of the data to be streamed to the file. So when you say
file.write("this is a message",3);
you tell the write function to write 3 bytes in the file. And that is "thi".
This
f.write((char*)&num,sizeof(char));
tells the write function to put sizeof(char) bytes in the file. That is 1 byte. You probably want it
f.write((char*)&num,sizeof(int));
as num is a int variable.
You are writing the ASCII string "thi" to file.dat. If you opened the file in a hex editor, you would see "74 68 69", which is the numeric representations of those characters. But if you open file.dat in an editor that understands ASCII, it will most likely translate those values back to their ASCII representation to make it easier to view. Opening the ofstream in ios::binary mode means that data is output to file as is, and no transformations may be applied by the stream before hand.
The function ofstream::write(const char *data, streamsize len) has two parameters. data is like this so that write is operating on individual bytes. That is why you have to cast num to a char* first. The second parameter, len, indicates how many bytes, starting from data that will be written to the file. My advise would be to use write(static_cast<char*>(num), sizeof(num)), then set num to be a type big enough to store the data required. If you declare int num, then on a 32bit platform, 20 zero bytes would be written to the file. If you only want 5 zero bytes, then declare as char num.