C++ Binary File to Structure Function - c++

I have a struct:
struct zipType{
int postalCode;
double longitude;
double latitude;
};
And I have a function called zipToCout:
void zipToCout(zipType zip){
cout << "Postal Code = " << zip.postalCode << "\tLongitude = " << zip.longitude << "\t\tLatitude = " << zip.latitude << endl;
}
now I need a function to read a binary file into a zipType struct. The function prototype is supposed to be void binRead(zipType *zip, fstream *input);. The only way I can get close to this is by changing the prototype to this void binRead(zipType &zip, fstream &input). With that, Here is what I currently have for the function:
void binRead(zipType &zip, fstream &input){
int temp;
double temp2;
zipType tempZip;
tempZip = zip;
//cout << "Reader at location " << input.tellg() << endl;
input.read((char*)&temp,sizeof(int));
tempZip.postalCode=temp;
input.read((char*)&temp2,sizeof(double));
tempZip.longitude=temp2;
input.read((char*)&temp2,sizeof(double));
tempZip.latitude=temp2;
zipToCout(tempZip);
}
This is the output I get when I run this on my sample.bin file:
Postal Code = 64501 Longitude = 2.61457e-261 Latitude = -7.13357e+288
What I need help with is reformatting the function to use *'s instead of &'s and fixing how to read the file into the three variables correctly. Thanks for looking! Also, I only have to read one zipType from the file at this point.

void binRead(zipType *zip, fstream *input)
{
input->read((char*)( &zip->postalCode ), sizeof(int ));
input->read((char*)( &zip->longitude ), sizeof(double));
input->read((char*)( &zip->latitude ), sizeof(double));
zipToCout(*zip);
}
Also, depending on the architecture (ie. 32-bit x86), the following may work:
void binRead(zipType *zip, fstream *input)
{
input->read((char*) zip, sizeof(zipType));
zipToCout(*zip);
}
That will only work on architectures where double only requires 4-byte alignment. I believe 32-bit x86 fits that. A quick test I wrote locally suggests that's the case.
A quick note on portable, maintainable code that goes beyond the immediate need above: Code such as the above works well enough when the machine saving the data is the same as the machine that later reads the data. It does, however, result in portability problems. If you really want to design a file format that's portable across machines and preserves data, the above techniques aren't really conducive to that.

I think the problem lies in while printing the values.
if you see the value is read in string and printed as other datatypes. I guess proper conversion function should work for you. and use see my comments below.
void binRead(zipType &zip, fstream &input){
char* temp = NULL
char* temp2 = NULL;
zipType tempZip;
tempZip = zip;
//cout << "Reader at location " << input.tellg() << endl;
input.read(temp,sizeof(int));
tempZip.postalCode=(atoi)temp; //use for proper conversion, or other function
input.read(temp2,sizeof(double));
tempZip.longitude=static_cast<double*>temp2; //use for proper conversion, or other function
input.read(temp2,sizeof(double));
tempZip.latitude=static_cast<double*>temp2;
zipToCout(tempZip);
}
Few comments on above code,
tempZip = zip; //why this, since you havn't declared any proper assignment
operator. Use memcpy instead.
tempZip.postalCode=(atoi)temp; //use for proper conversion, or other function
tempZip.longitude=static_cast<double*>temp2; //use for proper conversion, or other function
Let me know if this solves your problem.

Related

Returning and Printing Strings in C++

I am trying to get a file path from the user in the getPath() function and return the path as a string. I am having trouble because the compiler says i need to use const char's and i dont know how to do that. How would I use const chars and what even are they. Also how do I print them to the console like in the main function.
#include <iostream>
#include <stdio.h>
#include <string.h>
char getPath() {
char path[64];
std::cout << "Input File Name For Debugging:";
gets(path);
std::cout << "Debugging: ";
puts(path);
return path[64];
}
int main(){
char path[64];
int pathlen = strlen(reinterpret_cast<const char *>(path));
//suppost to print the char array
for(int i; i < pathlen; i++){
std::cout << path[i];
}
return 0;
}
Lot's of misunderstandings
1) char is not a string, it's a character
2) An array of chars (e.g. char [64]) is not a string, its an array. It can hold a string but that's a subtly different idea
3) You don't use [64] when you mean the whole array, so return path[64]; is not the correct way to return a string.
4) Don't mix C++ I/O (std::cin, std::cout) with C I/O (puts, gets), it doesn't work reliably, Stick with C++ I/O so
std::cout << "Debugging: " << path << '\n';
not
std::cout << "Debugging: ";
puts(path);
5) You never call your getPath function so of course it doesn't execute
6) You don't initialise your loop variable i in your final loop so it has no predictable value. You should initialise i to 0
for(int i; i < pathlen; i++){
std::cout << path[i];
should be
for(int i = 0; i < pathlen; i++){
std::cout << path[i];
As you can see lots and lots of mistakes for a very short program. I'm going to show two different correct ways to write this program.
So there are two ways to represent a string in C++, there's the C++ way and there's the way that C++ inherits from C. The code you are writing above is trying to do things the C way, so I'll show that first, but actually the C++ way is much much easier. I'll show that second, but it's the way you should do things.
The first way is to use an array of characters to hold the string. But arrays have serious problems in C++. In particular it's not possible to return an array from a function, so your code above was never going to work, even if you'd fixed all the smaller problems. The way you get C++ to 'return' an array is a bit curious and I'm not going to explain it properly (you need to read a good C++ book). What you do is declare the array in the calling function and pass the array as a parameter. Here's your program written using this technique (and fixed of all the other problems).
#include <iostream>
void getPath(char path[], int n) {
std::cout << "Input File Name For Debugging:";
std::cin.getline(path, n);
std::cout << "Debugging: " << path << '\n';
}
int main(){
char path[64];
getPath(path, 64);
std::cout << path << '\n';
return 0;
}
Note I'm using getline to read the string, which is one C++ way to read a string. getline requires that you pass the size of the array it's going to read into, so I've passed that to getPath as well as the array itself.
Now for the easy way. C++ has it's own string type called std::string. You don't need to use tricky arrays at all. And the C++ string type can be returned from a function in the normal way. This makes for much more natural code. To use the C++ string type all you need to do is #include <string>. Here's your program rewritten to use the C++ string type
#include <iostream>
#include <string>
std::string getPath() {
std::cout << "Input File Name For Debugging:";
std::string path;
std::getline(std::cin, path);
std::cout << "Debugging: " << path << '\n';
return path;
}
int main(){
std::string path;
path = getPath();
std::cout << path << '\n';
return 0;
}
Notice this second program is closer to your original code, getPath has a return type, only it's std::string not char, and it has a return statement to return the path. This is the way you should be writing this code, the C++ string type will make writing string code much easier for you.

C++ reading binary data to struct

I am currently reading a binary file that i know the structure of and i am trying to place into a struct but when i come to read off the binary file i am finding that when it prints out the struc individually it seems to come out right but then on the fourth read it seems to add it onto last member from the last read.
here the code which probably make's more sense than how i am explaining it:
Struc
#pragma pack(push, r1, 1)
struct header
{
char headers[13];
unsigned int number;
char date[19];
char fws[16];
char collectversion[12];
unsigned int seiral;
char gain[12];
char padding[16];
};
Main
header head;
int index = 0;
fstream data;
data.open(argv[1], ios::in | ios::binary);
if(data.fail())
{
cout << "Unable to open the data file!!!" << endl;
cout << "It looks Like Someone Has Deleted the file!"<<endl<<endl<<endl;
return 0;
}
//check the size of head
cout << "Size:" << endl;
cout << sizeof(head) << endl;
data.seekg(0,std::ios::beg);
data.read( (char*)(&head.headers), sizeof(head.headers));
data.read( (char*)(&head.number), sizeof(head.number));
data.read( (char*)(&head.date), sizeof(head.date));
data.read( (char*)head.fws, sizeof(head.fws));
//Here im just testing to see if the correct data went in.
cout<<head.headers<< endl;
cout<<head.number<< endl;
cout<<head.date<< endl;
cout<<head.fws<< endl;
data.close();
return 0;
Output
Size:
96
CF001 D 01.00
0
15/11/2013 12:16:56CF10001001002000
CF10001001002000
for some reason the fws seems to add to head.date? but when i take out the line to read head.fws i get a date that doesn't have anything added?
i also know thier more data to get for the header but i wanted to check the data up to what i have written is correct
cheers
1. Your date is declared as:
char date[19];
2. Your date format is exactly 19-characters long:
15/11/2013 12:16:56
3. And you print it this way:
cout<<head.date
Shortly speaking, you try to print fixed char[] using its address, which means, that it will be interpreted as null-terminated c-string. Is it null-terminated? No.
To solve this problem, declare date as:
char date[20];
And after you fill it, append null terminator:
date[19] = 0;
It applies to all members, that will be interpreted as string literals.
You have char date[19] filled with 15/11/2013 12:16:56 which is exactly 19 valid characters. This leaves no space for a terminating null and so doing cout << head.date outputs your 19 valid characters and then a load of garbage.

Writing an integer as hex into a File using ofstream

i have a function defined as follows:
void AddHeadCode(std::ofstream &ostream, size_t length){
ostream.write((char*)length, sizeof(length));
ostream.seekp(0x10L, std::ios::beg);
}
Now when this executes, it will fail obviously... as the char pointer will point nowhere.
But i want the actual pointervalue written into the file.
Like length = 19152
Then when I open up the file in an HEX Editor, I should find 0d4a there.
How can this be achieved in c++? Im kinda lost here...
Take the address of your length variable, and pass that address to .write:
void AddHeadCode(std::ofstream &ostream, size_t length){
// by popular demand, removed C-style cast
ostream.write(reinterpret_cast<char*>(&length), sizeof(length));
ostream.seekp(0x10L, std::ios::beg);
}
But, this is not usually refered to as writing an integer "as hex". This is sometimes referred to as writing an integer "as binary", or simply writing an integer.
Note that what you have done is not a portable practice. If you read the value back in on a different computer (or even on the same computer, but with a different compiler), you might not read in the same value.
Filestreams are tricky so I am uncertain about that but I use this for stringstreams:
std::basic_stringstream<wchar_t> oTmpStream;
oTmpStream << L"0x" << std::nouppercase << std::setfill( L'0' ) << std::hex << std::setw( 8 ) << iSomeValue
// or without fancy formatting;
oTmpStream << std::hex << iSomeValue

weird output when printing data of custom string (c++ newbie)

my main concern is if i am doing this safely, efficiently, and for the most part doing it right.
i need a bit of help writing my implementation of a string class. perhaps someone could help me with what i would like to know?
i am attempting to write my own string class for extended functionality and for learning purposes. i will not use this as a substitute for std::string because that could be potentially dangerous. :-P
when i use std::cout to print out the contents of my string, i get some unexpected output, and i think i know why, but i am not really sure. i narrowed it down to my assign function because any other way i store characters in the string works quite fine. here is my assign function:
void String::assign(const String &s)
{
unsigned bytes = s.length() + 1;
// if there is enough unused space for this assignment
if (res_ >= bytes)
{
strncpy(data_, s.c_str(), s.length()); // use that space
res_ -= bytes;
}
else
{
// allocate enough space for this assignment
data_ = new char[bytes];
strcpy(data_, s.c_str()); // copy over
}
len_ = s.length(); // optimize the length
}
i have a constructor that reserves a fixed amount of bytes for the char ptr to allocate and hold. it is declared like so:
explicit String(unsigned /*rbytes*/);
the res_ variable simply records the passed in amount of bytes and stores it. this is the constructor's code within string.cpp:
String::String(unsigned rbytes)
{
data_ = new char[rbytes];
len_ = 0;
res_ = rbytes;
}
i thought using this method would be a bit more efficient rather than allocating new space for the string. so i can just use whatever spaced i reserved initially when i declared a new string. here is how i am testing to see if it works:
#include <iostream>
#include "./string.hpp"
int main(int argc, char **argv)
{
winks::String s2(winks::String::to_string("hello"));
winks::String s(10);
std::cout << s2.c_str() << "\n" << std::endl;
std::cout << s.unused() << std::endl;
std::cout << s.c_str() << std::endl;
std::cout << s.length() << std::endl;
s.assign(winks::String::to_string("hello")); // Assign s to "hello".
std::cout << s.unused() << std::endl;
std::cout << s.c_str() << std::endl;
std::cout << s.length() << std::endl;
std::cout.flush();
std::cin.ignore();
return 0;
}
if you are concerned about winks::String::to_string, i am simply converting a char ptr to my string object like so:
String String::to_string(const char *c_s)
{
String temp = c_s;
return temp;
}
however, the constructor i use in this method is private, so i am forcing to_string upon myself. i have had no problems with this so far. the reason why i made this is to avoid rewriting methods for different parameters ie: char * and String
the code for the private constructor:
String::String(const char *c_s)
{
unsigned t_len = strlen(c_s);
data_ = new char[t_len + 1];
len_ = t_len;
res_ = 0;
strcpy(data_, c_s);
}
all help is greatly appreciated. if i have no supplied an efficient amount of information please notify me with what you want to know and i will gladly edit my post.
edit: the reason why i am not posting the full string.hpp and string.cpp is because it is rather large and i am not sure if you guys would like that.
You have to make a decision whether you will always store your strings internally terminated with a 0. If you don't store your strings with a terminating zero byte, your c_str function has to add one. Otherwise, it's not returning a C-string.
Your assign function doesn't 0 terminate. So either it's broken, or you didn't intend to 0 terminate. If the former, fix it. If the latter, check your c_str function to make sure it puts a 0 on the end.

Should we avoid repetitive code in C++ in order to be "Pythonic", and how?

I am in larval stage with Python and pre-egg stage in C++, but i am trying to do my best, specially with the "Don't Repeat Yourself" principle.
I have a multichannel raw file-format to open, with a main ascii header with fields representable as strings and integers (always coded as chars padded with white spaces). The second part is N headers, with N being a field of the main header, and each of those headers has itself a lot more of text and number fields (coded as ascii) refering to the length and size of the actual 16 bit multichannel streams that compose the rest of the file.
So far, I have this working code in C++:
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <map>
using namespace std;
struct Header {
string version;
string patinfo;
string recinfo;
string start_date;
string start_time;
int header_bytes;
string reserved;
int nrecs;
double rec_duration;
int nchannels;
};
struct Channel {
string label;
string transducertype;
string phys_dim;
int pmin;
int pmax;
int dmin;
int dmax;
string prefiltering;
int n_samples;
string reserved;
};
int main()
{
ifstream edf("/home/helton/Dropbox/01MIOTEC/06APNÉIA/Samples/Osas2002plusQRS.rec", ios::binary);
// prepare to read file header
Header header;
char buffer[80];
// reads header fields into the struct 'header'
edf.read(buffer, 8);
header.version = string(buffer, 8);
edf.read(buffer, 80);
header.patinfo = string(buffer, 80);
edf.read(buffer, 80);
header.recinfo = string(buffer, 80);
edf.read(buffer, 8);
header.start_date = string(buffer, 8);
edf.read(buffer, 8);
header.start_time = string(buffer, 8);
edf.read(buffer, 8);
stringstream(buffer) >> header.header_bytes;
edf.read(buffer, 44);
header.reserved = string(buffer, 44);
edf.read(buffer, 8);
stringstream(buffer) >> header.nrecs;
edf.read(buffer,8);
stringstream(buffer) >> header.rec_duration;
edf.read(buffer,4);
stringstream(buffer) >> header.nchannels;
/*
cout << "'" << header.version << "'" << endl;
cout << "'" << header.patinfo << "'" << endl;
cout << "'" << header.recinfo << "'" << endl;
cout << "'" << header.start_date << "'" << endl;
cout << "'" << header.start_time << "'" << endl;
cout << "'" << header.header_bytes << "'" << endl;
cout << "'" << header.reserved << "'" << endl;
cout << "'" << header.nrecs << "'" << endl;
cout << "'" << header.rec_duration << "'" << endl;
cout << "'" << header.nchannels << "'" << endl;
*/
// prepare to read channel headers
int ns = header.nchannels; // ns tells how much channels I have
char title[16]; // 16 is the specified length of the "label" field of each channel
for (int n = 0; n < ns; n++)
{
edf >> title;
cout << title << endl; // and this successfully echoes the label of each channel
}
return 0;
};
Some remarks I already have to make:
I opted to use struct because the format specification is very hardcoded;
I didn't iterate over the main header fields because the number of bytes and types to read seemed to me rather arbitrary;
Now that I successfully got each channel's label, I would actually create structs for each channel's fields, which by themselves would have to be stored perhaps in a map.
My (hopefully straightforward) question is:
"Should I worry about cutting corners to make this kind of code more 'Pythonic' (more abstract, less repetitive), or this is not the way things work in C++?"
Many Python evangelists (as I would be myself, because I love it) highlight its easyness to use and all that. So, I will wonder for some time if I am doing dumb things or only doing things right, but not so "automagical" because of the very nature of C++.
Thanks for reading
Helton
I'd say there's no such thing as Pythonic C++ code. The DRY principle applies in both languages, but much of what is considered "Pythonic" is simply the shortest, sweetest way of expressing logic in Python, using Python-specific constructs. Idiomatic C++ is quite different.
lambda, for example, is sometimes not considered very Pythonic and reserved for cases where no other solution exists, but is just being added to the C++ standard. C++ has no keyword arguments, which are very Pythonic. C++ programmers don't like constructing a map when not necessary, while a Python programmer might throw dict at a lot of problems where they just happen to make the intention clearer than the efficient alternative.
If you want to save typing, use the function I posted earlier, then:
header.version = read_field(edf, 8);
header.patinfo = read_field(edf, 80);
That should save you quite a few lines. But more important than those few lines is that you've achieved a small amount of modularity: how to read a field and what fields to read are now separate parts of your program.
You are correct: as written, the code is repetitive (and has no error checking). Each field that you read really requires you to take three or five steps, depending on the type of data being read:
Read the field from the stream
Ensure the read succeeded
Parse the data (if necessary)
Ensure the parse succeeded (if necessary)
Copy the data into the target location
You can wrap all three of these up into a function so that the code is less repetitive. For example, consider the following function templates:
template <typename TStream, typename TResult>
void ReadFixedWidthFieldFromStream(TStream& str, TResult& result, unsigned sz)
{
std::vector<char> data(sz);
if (!str.read(&data[0], sz))
throw std::runtime_error("Failed to read from stream");
std::stringstream ss(&data[0]);
if (!(ss >> result))
throw std::runtime_error("Failed to parse data from stream");
}
// Overload for std::string:
template <typename TStream>
void ReadFixedWidthFieldFromStream(TStream& str, std::string& result, unsigned sz)
{
std::vector<char> data(sz);
if (!str.read(&data[0], sz))
throw std::runtime_error("Failed to read from stream");
result = std::string(&data[0], sz);
}
Now your code can be much more succinct:
ReadFixedWidthFieldFromStream(edf, header.version, 8);
ReadFixedWidthFieldFromStream(edf, header.patinfo, 80);
ReadFixedWidthFieldFromStream(edf, header.recinfo, 80);
// etc.
This code is straightforward, simple, and easy to understand. If it's working, don't waste time changing it. I'm sure there's plenty of badly written, complex, and difficult to understand (and probably incorrect) code that should be fixed first :)
The Zen of Python doesn't mention DRY explicitly.
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
For reading from file directly in strings see this question The rest is wrong. but personally I think there's a better/cleaner way of doing this.
If you know the size of the structure don't use string, use primitive C types (and make sure the structure is packed). See these links: http://msdn.microsoft.com/en-us/library/2e70t5y1(v=vs.80).aspx & http://gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Type-Attributes.html
I would do it this way for example (not sure about the size of each string but you get the idea):
struct Header {
char version[8];
char patinfo[80];
char recinfo[80];
char start_date[8];
char start_time[8];
int header_bytes;
char reserved[44];
int nrecs;
double rec_duration;
int nchannels;
};
Once you have a packed structure you can read it directly from the file:
struct Header h;
edf.read(&h,sizeof(struct Header));
For me this is the cleanest way to do it, but remember you must have your structure packed so that you have the guarantee that the structure in memory has the same size as the structure saved in the file - this is not very hard to see while testing.