Reading binary data into struct with ifstream - c++

I'm trying to read binary data from a file using ifstream.
Specifically, I'm trying to populate this "Header" struct with data read from a file:
struct Header {
char id[16];
int length;
int count;
};
Now, if I read the file in this way, the result is exactly what I want:
input.read((char*)&hdr, sizeof(hdr));
But if I instead read each variable of the struct manually, the results are gibberish:
input.read((char*)&hdr.id, sizeof(hdr.id));
input.read((char*)&hdr.length, sizeof(hdr.length));
input.read((char*)&hdr.count, sizeof(hdr.count));
My question is, what is happening here that makes these two methods return different results?

It is also possible to read the struct in one step.
i.e. fh.read((char*)&h, sizeof(Header));

As the comment above states, you are probably missing hdr.length and hdr.count.
I tried it with gcc 4.8 and clang 3.5 and it works correctly.
#include <iostream>
#include <fstream>
#pragma pack(push, r1, 1)
struct Header {
char id[15];
int length;
int count;
};
#pragma pack(pop, r1)
int main() {
Header h = {"alalalala", 5, 10};
std::fstream fh;
fh.open("test.txt", std::fstream::out | std::fstream::binary);
fh.write((char*)&h, sizeof(Header));
fh.close();
fh.open("test.txt", std::fstream::in | std::fstream::binary);
fh.read((char*)&h.id, sizeof(h.id));
fh.read((char*)&h.length, sizeof(h.length));
fh.read((char*)&h.count, sizeof(h.count));
fh.close();
std::cout << h.id << " " << h.length << " " << h.count << std::endl;
}

Related

Pass a pointer to a file buffer to a class, expecting to read from file inside a class

I want to learn how to search in the file by passing the pointer of the stream to a class.
I can successfully get the first character from the file using std::fstream and std::filebuf*
char symbol;
std::fstream by_fstream;
by_fstream.open("First_test_input.txt");
std::filebuf* input_buffer = by_fstream.rdbuf();
symbol = input_buffer -> sbumpc();
std::cout << "\nSymbol that get from a file by rdbuf(): " << symbol;
Output: Symbol that get from a file by rdbuf(): M
But I'm not sure how can I send any pointer to my original stream of the file from main to a class.
Ideally, it would be great to do something like this:
#include <iostream>
#include <fstream>
class from_file
{
public:
char c;
from_file () {
std::cout << "\nCharacter that get from file by to class variable"
<<" then printed: " << c;
};
from_file (char *pointer){
c = pointer -> sbumpc();
};
~from_file ();
};
int main(){
std::fstream by_fstream;
by_fstream.open("First_test_input.txt");
std::filebuf* input_buffer = by_fstream.rdbuf();
from_file send(&input_buffer);
from_file show;
return 0;
}
Looking for advice on where I can find documentation about similar headers to do a such task.
You are going about this all wrong.
First off, you should pass around (a reference to) the stream itself, not its internal buffer. Use std::istream methods like read() or get() or operator>> to read from the stream, let it handle it own buffer for you.
Secondly, you are trying to make a 2nd completely separate object "magically" know what a previous object is holding. That is not going to work out the way you want, either.
Try something more like this instead:
#include <iostream>
#include <fstream>
class from_stream
{
public:
char c;
from_stream (std::istream &in){
c = in.get();
// or: in.get(c);
// or: in.read(&c, 1);
// or: in >> c;
};
void show() const {
std::cout << "\nCharacter that get from file by to class variable"
<<" then printed: " << c;
}
};
int main(){
std::ifstream by_ifstream;
by_ifstream.open("First_test_input.txt");
from_stream send(by_ifstream);
send.show();
return 0;
}

How to read hex values into integer with fstream (C++)

I am trying to read a little-endian hex string from a binary file, and put that value into an integer to work with it. When I try to read, instead of getting a number I get ascii symbols. I've tried casts and atoi and nothing seems to work. What is the best way to use fstream to read a hex string into an integer from a file?
This is essentially my program:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char* argv[]) {
fstream input;
fstream output;
char cbuffer[4];
char revbuffer[8];
input.open(argv[1], fstream::binary | fstream::in);
output.open("output.txt", ios::out | ios::app);
input.seekg(16, input.beg);
input.read(cbuffer, 4);
cout << sizeof(revbuffer) << endl;
cout << cbuffer[0] << cbuffer[1] << cbuffer[2] << cbuffer[3] << endl;
}
If it's an integer value stored in binary format, I guess it's either a int32_t or a uint32_t. Since you mention that the value is stored in little-endian byte order, I guess you want to make sure that the host running your program converts it (if it needs to). C++20 has std::endian. If that's not available to you, there are usually macros for detecting endianness at compiletime that you can use instead of the std::endian tests I've used. I've assumed that the value is a uint32_t below.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <type_traits> // std::endian
// little endian unsigned 32 bit integer to host byte order
inline uint32_t Le32toh(uint32_t le) {
#if __cplusplus <= 201703L
// run-time check
static constexpr uint16_t endian = 1;
if(*reinterpret_cast<const uint8_t*>(&endian)==1) return le;
#else
// compile-time check
static_assert(std::endian::native == std::endian::little || std::endian::native == std::endian::big);
if constexpr (std::endian::native == std::endian::little) return le;
#endif
const uint8_t* c=reinterpret_cast<const uint8_t*>(&le);
return // little-to-big endian conversion
(static_cast<uint32_t>(c[0])<<24) |
(static_cast<uint32_t>(c[1])<<16) |
(static_cast<uint32_t>(c[2])<<8) |
(static_cast<uint32_t>(c[3]));
return le;
}
int main(int argc, char* argv[]) {
std::vector<std::string> args(argv+1, argv+argc);
std::fstream output("output.txt", std::ios::out | std::ios::app);
uint32_t cbuffer;
for(const auto& file : args) {
std::fstream input(file, std::fstream::binary | std::fstream::in);
input.seekg(16, input.beg);
// read directly into the varibles memory
input.read(reinterpret_cast<char*>(&cbuffer), 4);
// output the value unconverted
std::cout << std::hex << cbuffer << "\n";
// convert if needed
cbuffer = Le32toh(cbuffer);
// output the value converted
std::cout << std::hex << cbuffer << "\n";
}
}

C++ cereal de-serialization trouble with large size vector

I hope to serialize large size vector with cereal, C++ serialization library.
But, if trying to do that, the exception "Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize)" is thrown.
Does anyone know a good solution for this?
I'm using VisualStudio 2017.
The source code is shown below.
#include <iostream>
#include <fstream>
#include "include\cereal\cereal.hpp"
#include "include\cereal\archives\binary.hpp"
#include "include\cereal\types\vector.hpp"
#include "include\cereal\types\string.hpp"
void show(std::vector<int> v) {
for (auto i : v)std::cout << i << ",";
std::cout << std::endl;
}
int main(void) {
const std::string file_name = "out.cereal";
{
std::vector<int> src;
// const int STOP = 10; //OK
const int STOP = 1000; // NG
for (int i = 0; i < STOP; i++)src.push_back(i);
std::cout << "src:" << std::endl;
show(src);
std::ofstream ofs(file_name, std::ios::binary);
cereal::BinaryOutputArchive archive(ofs);
archive(src);
}
{
std::vector<int> dst;
std::fstream fs(file_name);
cereal::BinaryInputArchive iarchive(fs);
iarchive(dst);
std::cout << "dst:" << std::endl;
show(dst);
}
#ifdef _MSC_VER
system("pause");
#endif
return 0;
}
You code works fine for me in Linux, so I think it is to do with the difference between text and binary handling on Windows. Check that you pass std::ios::binary when you are constructing the input stream. Also construct it as std::ifstream rather than just std::fstream.
I think this might have to do with Windows expecting (or adding) a Unicode byte-order mark, which is confusing the serializer.

How to convert binary files context into int/long value in C++

I am storing a value in a binary file with fstream. The value is unsigned short type.
unsigned shord value=1750; //2 byte variable
file.write((char*)&value,sizeof(value));
My problem is that I want to read this binary file in another function, but it gives me some weird symbols (Obviously because it is binary).
Is there any way to get those two bytes and convert them to my old value (1750) ?
Here's what I have tried:
cout <<(unsigned short)(unsigned char)(s2[8]);//s2 variable where the whole body is stored
cout <<(unsigned short)(char*)(s2[8]);
I have tried other things, too, but they were just chickenscratch and aren't worth including here.
Here's how you might do it (note uses C++11 features):
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
void write_shorts(string filename, vector<unsigned short> shorts)
{
ofstream f;
f.open(filename, ofstream::trunc|ofstream::binary);
for(auto s: shorts)
f.write(reinterpret_cast<char*>(&s), sizeof(s));
}
auto read_shorts(string filename)
{
ifstream f(filename);
vector<unsigned short> res;
short x;
while(f.read(reinterpret_cast<char*>(&x), sizeof(x)))
res.push_back(x);
return res;
}
int main()
{
// Write 3 shorts to a file
write_shorts("myshorts", {4711, 1, 0xffff});
// Read them back into a vector
auto v = read_shorts("myshorts");
cout << "Read " << v.size() << "shorts: " << endl;
for(auto x: v)
cout << x << endl;
}

C++ templates containing std::vector

Very recently I wrote a class containing four functions to open and read multicolumn data files (up to 4 columns). In the function the name of the file to be opened "file_name" is passed from the main program to the function in class "Read_Columnar_File". The data is read in using std::vector and passed back to the main program. However, it required that the programmer change the data types of the input columns every time it is used which is a recipe for errors. The file name will always be a character string, so that does not need to be templated; however, the data type of the arrays read in using vector can change, so that needs to be generically templated. I am trying to convert the class to a template class and am missing some fundamental understanding of the process with respect to making templates containing std::vector. To simplify the development process I have gone back to a single routine titled "Read_One_Column" inside the class and am trying to convert it a template where the data type is labeled as Type1. I think my problem is in the syntax since the debugger is telling me that the command in the main program is undefined. Any advice to help correct this would be appreciated. A copy of the existing code is attached below.
#include <vector>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <iterator>
template <class Type1> class Read_Columnar_File {
public:
void Read_One_Column(const std::string& file_name,std::vector<Type1>& Column1);
};
template <class Type1> void Read_Columnar_File<Type1>::Read_One_Column(const std::string& file_name,
std::vector<Type1>& Column1)
{
std::ifstream inp(file_name,std::ios::in | std::ios::binary);
std::istream_iterator<Type1> start((inp)), end;
if(inp.is_open()) {
Column1.assign(start,end);
}
else std::cout << "Cannot Open " << file_name << std::endl;
inp.close();
}
int main(int argc, const char * argv[]) {
int i;
std::vector<float> str1;
Read_Columnar_File<float> ob1;
char str[20];
std::strcpy(str,"Test.txt");
ob1.Read_One_Column(str,str1);
for(i=0; i < 7; i++) std::cout << str1[i] << std::endl;
return 0;
}
The syntax is simpler:
template <typename Type1>
void Read_One_Column(const std::string& file_name,
std::vector<Type1>& Column1) {
...
}
no need to create a class at all (it's just a template function).
If you need to put the function in a class for other reasons then the syntax is the same
struct Read_Columnar_File {
...
template<typename Type1>
void Read_One_Column(const std::string& file_name,
std::vector<Type1>& Column1) {
...
}
}
and it will be a template method of the class.
To fully close out this question I am posting the final and correct code since I am sure others will have the same question in the future and i hope this helps them. To answer my question, when programming a template, the entire algorithm needs to be included in the header and cannot be split between a header and implementation file. This program is meant to be a very generic method of reading in columnar data from an input file and assumes that each column of data is the same length as the others. The user can merely like the header file to their main program, specify the data type of each column in the vector definition and read in the data. The main program is shown below. This version allows the user to call 4 different functions which can be used to read in up to as much as four columns of data.
#include <vector>
#include <iostream>
#include <cstring>
#include "Read_Columnar_File.h"
int main(int argc, const char * argv[]) {
char str[20];
strcpy(str,"Test.txt");
// - Format for reading in a single column of data
// Data in this case is declared as a float in
// the vector, but it can be any data type
/*
std::vector<float> str2;
Read_One_Column(str,str2);
*/
// - Format for reading in two columns of data from
// an input file
/*
std::vector<float> str2;
std::vector<int> str3;
Read_Two_Columns(str,str2,str3);
*/
// - Format for reading in three columns of data from
// an input file
/*
std::vector<float> str2;
std::vector<int> str3;
std::vector<int> str4;
Read_Three_Columns(str,str2,str3,str4);
*/
std::vector<float> str2;
std::vector<int> str3;
std::vector<int> str4;
std::vector<float> str5;
Read_Four_Columns(str,str2,str3,str4,str5);
return 0;
}
The implementation file is shown below.
#include <vector>
#include <stdio.h>
#include <fstream>
#include <iterator>
template <class X> void Read_One_Column(const std::string& file_name,std::vector<X>& Column1)
{
std::ifstream inp(file_name,std::ios::in | std::ios::binary);
std::istream_iterator<X> start((inp)), end;
if(inp.is_open()) {
Column1.assign(start,end);
}
else std::cout << "Cannot Open " << file_name << std::endl;
inp.close();
}
template <class X,class Y> void Read_Two_Columns(const std::string& file_name,std::vector<X>& Column1,
std::vector<Y>& Column2)
{
int i;
X Col1;
Y Col2;
std::ifstream inp(file_name,std::ios::in | std::ios::binary);
if(inp.is_open()){
for(i=0; i < 7; i++){
inp >> Col1 >> Col2;
Column1.push_back(Col1), Column2.push_back(Col2);
}
}
else std::cout << "Cannot Open " << file_name << std::endl;
inp.close();
}
template <class X,class Y, class Z> void Read_Three_Columns(const std::string& file_name,std::vector<X>& Column1,
std::vector<Y>& Column2,std::vector<Z>& Column3
{
int i;
X Col1;
Y Col2;
Z Col3;
std::ifstream inp(file_name,std::ios::in | std::ios::binary);
if(inp.is_open()){
for(i=0; i < 7; i++){
inp >> Col1 >> Col2 >> Col3;
Column1.push_back(Col1), Column2.push_back(Col2), Column3.push_back(Col3);
}
}
else std::cout << "Cannot Open " << file_name << std::endl;
inp.close();
}
template <class X,class Y, class Z,class A> void Read_Four_Columns(const std::string& file_name,std::vector<X>& Column1,
std::vector<Y>& Column2,std::vector<Z>& Column3,
std::vector<A>& Column4)
{
int i;
X Col1;
Y Col2;
Z Col3;
A Col4;
std::ifstream inp(file_name,std::ios::in | std::ios::binary);
if(inp.is_open()){
for(i=0; i < 7; i++){
inp >> Col1 >> Col2 >> Col3 >> Col4;
Column1.push_back(Col1), Column2.push_back(Col2),
Column3.push_back(Col3), Column4.push_back(Col4);
}
}
else std::cout << "Cannot Open " << file_name << std::endl;
inp.close();
}