Writing integers to binary files

Hello all,
I am currently working on a program that reads in from a txt file and outputs data to a binary file. The input file consists of both strings and integers (all of which are 1 byte). As of now, I can read and write strings from the files as well as write some integers to the binary file. However, when I read in a uint8_t from the text file and try writing it to the binary file, it writes the ascii table value. For reference, here is what my code looks like:
1
2
3
4
5
6
7
8
9
10
11
12
13
ifstream inputFile;
ofstream outputFile;
uint8_t nTerritories;
string ifName, mapName, ofName;

inputFile.open(ifName, ios::binary);
inputFile >> mapName >> nTerritories;
outputFile.open(ofName, ios::binary | ios::out);
uint8_t mapNameSize = mapName.size() + 1;

outputFile.write(reinterpret_cast<char*> (&mapNameSize), 1);
outputFile.write((mapName.c_str()), sizeof(mapName.c_str()) - 2);
outputFile.write(reinterpret_cast<const char *>(&nTerritories), 1);


ifName and ofName are both file names supplied by the user while mapName stores the first string in the file. I omitted these due to relevance.

I'm quite new to the binary file IO so any help would be much appreciated. within the input file, the relevant input is "Alaska 1". The code, as is, is writing Alaska'\0''1' to the binary file when I would like it to write Alaska'\0'1. If I include the line nTerritories = 1; before the write() lines, it works as intended.

Edit: I am also not checking if files exist since I know they do. I will be sure to add this once I have it working!
Last edited on
sizeof(mapName.c_str()) is not what you want here.
You're doing sizeof on a pointer, so it's going to be the same value regardless of your string length.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ cat foo.cpp
#include <fstream>
#include <string>
#include <cstdint>
using namespace std;
int main ( ) {
  ofstream outputFile;
  outputFile.open("foo.dat", ios::binary | ios::out);
  string mapName = "California";
  uint8_t nTerritories = 1;
  uint8_t mapNameSize = mapName.size() + 1;

  outputFile.write(reinterpret_cast<char*> (&mapNameSize), 1);
  outputFile.write((mapName.c_str()), mapNameSize);
  outputFile.write(reinterpret_cast<const char *>(&nTerritories), 1);
}
$ g++ -std=c++11 foo.cpp
$ ./a.out 
$ hd foo.dat
00000000  0b 43 61 6c 69 66 6f 72  6e 69 61 00 01           |.California..|
0000000d

Writing both the string length and the \0 are redundant.
Normally, you pick one or the other.
Yes, I know it's redundant but the binary file is being used for a different program I'm writing for the TI 84+ CE which doesn't have a read until null byte function and I felt comfortable sacrificing one extra byte to allow me to know how many bytes to read (although looking at it now I could initialize it with a null terminator). I updated my code to reflect your sizeof comment which I am thankful for. However, the nTerritories issue continues to exist, but only when setting its value from the input file (as seen on line 7 of my original post), giving a binary file of
52 4D 50 06 57 6F 72 6C 64 00 31
The last byte, 0x31, should read 0x01 rather than the ascii code for '1'. I did also verify nTerritories was 1 by cout-ing its value before writing to the file.

Let me know if you may know the cause of this!
Last edited on
No idea, your hex dump is garbage.
06 for example isn't a printable character.

1
2
3
$ hd foo.dat
00000000  07 41 6c 61 73 6b 61 00  01                       |.Alaska..|
00000009
Oops, I had some other code I was testing writing with to see if I could replicate the issue and was writing "World" instead of "Alaska". Here's what mine is giving using Alaska:

07 41 6C 61 73 6B 61 00 31 |.Alaska.1|

Is there some flag that causes ifstreams to give integers the ascii value of a character rather than the number written? My code is getting its values from the input file rather than them being hard-coded.
unit8_t for the purpose of stream extraction is treated as an unsigned char and the data is read as such eg 1 is read as char '1' and not the number 1. The easiest way is probably to cast the extraction to an unsigned. Possibly (not tried) for L7:

 
inputFile >> mapName >> static_cast<unsigned>(nTerritories);

Last edited on
you can also convert it on the way out, subtract '0' from the character '1' and you get the integer 1.
> unit8_t for the purpose of stream extraction is treated as an unsigned char
> and the data is read as such eg 1 is read as char '1' and not the number 1.
That's one awful nasty little gotcha.

It says 'int' in the type, one might reasonably expect it to behave in a manner compatible will all other 'int' types.
seeplus wrote:
unit8_t for the purpose of stream extraction is treated as an unsigned char and the data is read as such eg 1 is read as char '1' and not the number 1. The easiest way is probably to cast the extraction to an unsigned.

Wow, I never knew this. The code you gave doesn't seem to compile, but I am thinking of inputting as an int and then casting to uint8_t since I know all data being used will be able to fit in a uint8.
jonnin wrote:
you can also convert it on the way out, subtract '0' from the character '1' and you get the integer 1.

I would do this, but the inputted number can be greater than 10 which I believe would not work as it would still go character by character.

Another option I am seeing online is operator overloading. I don't have much time to look into this but I will be sure to update this thread once I have worked on it.
read as int and downcast is the right way from the sound of it.
It says 'int' in the type, one might reasonably expect it to behave in a manner compatible will all other 'int' types.


uint8_t is actually a typedef for unsigned char. Same as int8_t is a typedef for char.

That's why stream extraction/insertion treat them as char and not int.

The other intnn_t types are similarly typedef for appropriate base types.

From stdint.h:

1
2
3
4
5
6
7
8
typedef signed char        int8_t;
typedef short              int16_t;
typedef int                int32_t;
typedef long long          int64_t;
typedef unsigned char      uint8_t;
typedef unsigned short     uint16_t;
typedef unsigned int       uint32_t;
typedef unsigned long long uint64_t;

Last edited on
Topic archived. No new replies allowed.