Parsing header of file - c++

I am working with images. I would like to extract width and height of it from a header.
The width is represented at location
size
4B width
4B height
they are represented at specific index.
I tried parse it and extract it with code
ifstream f(name, ios::binary | ios:: in ); // reading a file in binary
ostringstream ob;
if ( f.fail()){ // fail test
return false;
}
f.seekg (0, f.end);
int length = f.tellg(); // length of the file
memory = new char[length]; // allocate array of chars
f.read (memory, length); // read the content of the file into an array
f.seekg (0, f.beg); // point back at the beginning of the file.
Each of them has 4B , so using for loop
for ( int i = index ; i <4 ;i++){
cout << hex<< memory[i];
}
or i even tried it converting it into number using
string a;
for ( int i = index; i < 4 ;i++{
a+=memory[i];
}
cout << atoi( a.c_str() ) << endl;
Should output a number , but it output some unreadable format.

You're seeking to the beginning of the file after reading it, rather than before. You need to switch round the read() and the seekg():
f.seekg (0, f.beg); // point back at the beginning of the file.
f.read (memory, length); // read the content of the file into an array

You don't give very much information to go on so a little guesswork here but this is how I might approach reading the file:
The main features being, don't use manual memory allocation, use a std::vector (its what its for). Also copy the data from the char array into variables cast from the correct type. This ensures alignment is correct (never cast into the char array). Another approach might be to read directly from the file into the correctly typed variables cast into char*.
int main(int, char* argv[])
{
// first parameter needs to be file name
std::string name = argv[1] ? argv[1]:"";
std::ifstream ifs(name, std::ios::binary|std::ios::ate); // open at end
if(!ifs)
{
std::cerr << std::strerror(errno) << '\n';
return EXIT_FAILURE;
}
if(ifs.tellg() < 8) // too small
{
std::cerr << "Bad image file, too short" << '\n';
return EXIT_FAILURE;
}
// Don't allocate memory manually, use a container
std::vector<char> image(ifs.tellg()); // big enough for whole file
ifs.seekg(0); // back to beginning
if(!ifs.read(image.data(), image.size()))
{
std::cerr << std::strerror(errno) << '\n';
return EXIT_FAILURE;
}
// copy raw data into variables
std::uint32_t width; // 4 bytes wide integer
std::uint32_t height; // 4 bytes wide integer
std::copy(&image[4], &image[ 8], (char*)&width); // offset 4 bytes
std::copy(&image[8], &image[12], (char*)&height); // offset 8 bytes
// at this point a lot depends on the system architecture and
// how the number is stored in the file. The documentation
// should tell you if it is little-endian or big-endian
// you may have to do manual jiggery-pokery
// to change endienness
std::cout << "width : " << width << '\n';
std::cout << "height: " << height << '\n';
}

For a quick answer of the problem stated in the title, and by looking at the input file you specify, you could simply read three lines, skip the first and then extract one integer each from the following two lines.
So something like
int width, height;
std::string input;
std::istringstream is;
std::getline(stream, input); // One line for the "size" string
std::getline(stream, input); // One line for the width
is.str(input)
is >> width;
std::getline(stream, input); // One line for the height
is.str(input)
is >> height;
If you have multiple entries like this in the file, then do the above in a loop.
If the file doesn't actually contain the texts you show, just the numbers, then it's even simpler:
int width, height;
stream >> width >> height;
Or if you have multiple entries
while (stream >> width >> height) { ... }

std::cout will print data passed as characters if the data is char, so try casting before printing.
for ( int i = 0 ; i < 4 ; i++) {
cout << hex<< (int)(unsigned char)memory[index + i];
}
If the number in the file is little endian, it can be converted to integer like this:
// Use #include <cstdint> for using type uint32_t and uint8_t
uint32_t num = 0; // the converted number will be here
for ( int i = 0 ; i < 4 ; i++) {
num |= (uint8_t)memory[index + i] << (uint32_t)(8 * i);
}

Related

Weird characters appear at the end of file when encrypting it

I never thought I would have to turn to SO to solve this.
Alright so for more insight I am making my own encryption program.
I'm not trying to make it good or anything it's just a personal project.
What this program is doing is that it's flipping certain bits in every single byte of the character making it unreadable.
However every time I run the program and decrypt I get weird characters on the output. These characters seem to match the amount of lines as following:
^^ text that I want to encrypt
^^ after encrypting. (a lot of the text got cut off)
^^ after decrypting. there's 10 null character corresponding to the amount of newlines. there also seems to be another weird '�' character. Where are these bytes coming from??
I've tried a lot of stuff. Here is my code if anyone needs it (it's compiled with default flags):
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#define ENCRYPTFILE "Encrypted.oskar"
typedef unsigned char BYTE;
char saltFunc(BYTE salt, char chr) {
for(int i = 0; i < 8; i++) {
if((salt >> i) & 1U) {
chr ^= 1UL << i;
}
}
return chr;
}
int main () {
std::ofstream encryptFile(ENCRYPTFILE, std::ifstream::in);
std::ifstream inputFile(ENCRYPTFILE, std::ifstream::in);
unsigned int length;
unsigned int lineLength;
BYTE salt = 0b00000001;
std::string line;
std::cin.unsetf(std::ios::dec);
std::cin.unsetf(std::ios::hex);
std::cin.unsetf(std::ios::oct);
//std::cout << "input salt in hex with a prefix 0x so for example. 0xA2" << std::endl;
//std::cin >> std::hex >> salt;
inputFile.seekg(0, inputFile.end);
length = inputFile.tellg();
inputFile.seekg(0, inputFile.beg);
std::cout << lineLength << std::endl;
char* fileBuffer = new char[length];
char* encryptFileBuffer = new char[length];
memset(fileBuffer, 0, length);
memset(encryptFileBuffer, 0, length);
while (inputFile.good()) { // just get file length in bytes.
static int i = 0;
fileBuffer[i] = inputFile.get();
i++;
}
while (std::getline(inputFile, line))
++lineLength;
inputFile.clear();
encryptFile.clear();
std::cout << "file size: " << length << std::endl;
for(int i = 0; i < length; i++) {
encryptFileBuffer[i] = saltFunc(salt, fileBuffer[i]);
encryptFile << encryptFileBuffer[i];
}
inputFile.close();
encryptFile.close();
delete[] encryptFileBuffer;
delete[] fileBuffer;
return 0;
}
The problem is that you are measuring the length of the file in bytes, which, for text files, is not the same as the length in characters. But you are then reading it as characters, so you end up reading too many characters and then writing extra garbage after then end in the output file.
Since you are getting one extra character per line, it is likely you are running on Windows, where line ending characters are two bytes in the file. That's where the extra incorrect length you are seeing is coming from.
For encryption/decryption what you probably want to do is read and write the file in binary mode, so you are reading and writing bytes not characters. You do this by adding std::ios::binary into the flags when opening the file(s):
std::ofstream encryptFile(ENCRYPTFILE, std::ifstream::in | std::ios::binary);
std::ifstream inputFile(ENCRYPTFILE, std::ifstream::in | std::ios::binary);

How to read any kind of files as binary and edit it as you want (like compress it) with c++?

I'm trying to figure out a way to manipulate the binary code of any file in the computer in goal to apply a compress/decompress algorithm in c++ .
I have been searching about that for long time and all i found was how to read a .bin file :
#include <iostream>
#include <fstream>
using namespace std;
int main (){
streampos size;
char * memblock;
ifstream file ("name.bin", ios::in|ios::binary|ios::ate);
if (file.is_open())
{
size = file.tellg();
memblock = new char[size];
file.seekg (0, ios::beg);
file.read (memblock, size);
for(int i = 0 ; i < size ; i++){
cout << memblock[i] ;
}
file.close();
cout << "\n\n the entire file content is in memory";
delete[] memblock;
}
else cout << "Unable to open file";
return 0;
}
I just wanna those bytes without ASCII translation, other words i wanna all the file as binary not what is inside it
<< is overloaded for char types to output the ASCII formated character. The data (the ones and zeros) in your memblock array are accurately read in as binary. It's just the way you're displaying them that is ASCII. Instead of a char[] for memblock, make it a uint8_t[]. Then, when you output, do
std::cout << std::hex << std::fill('0') << std::setw(2) << memblock[i];
^ ^ ^
| | |
| | sets the width of the next output
| sets the fill character (default is space)
tells the stream to output all numbers in hexadecimal
You'll have to #include <iomanip> for the stream format manipulators hex, fill, and setw to work.
Note that setw will only be set on the stream for the next output operation, while hex and fill will be set until explicitly set otherwise. That said, you only need to set these two manipulators once, probably outside your loop. Then when you're finished, you can set them back like:
std::cout << std::dec << std::fill(' ');
See https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt2 for the list of overloaded operator<< functions for char and char arrays.
The answer was simpler than we thought :
include bitset
for(int i = 0 ; i < size ; i++){
//changing the value of "memblock[i]" to binary byte per byte with for loop
//and of course using bitset
bitset<8> test (memblock[i]);
cout << test ;
}

c++ read and write struct array with std::string into binary file

I'm very new to c++ (and the question is a part of my homework).
I need to store an array of structs in a binary file and read them back. The problem is that my struct contains std::string field. On the other side I can read the string, but nothing after it. Here is my structure definition:
struct Organization {
int id;
string name;
float paidTaxes;
};
and here is the code where I write array of this struct to a file:
void writeToFile(Organization *orgs, int n) {
ofstream file("orgs.bin", ios::out | ios::binary);
if (!file) { return; }
for (int i = 0; i < n; i++) {
// determine the size of the string
string::size_type sz = orgs[i].name.size();
file.write(reinterpret_cast<char*>(&orgs[i].id), sizeof(int));
// write string size
file.write(reinterpret_cast<char*>(&sz), sizeof(string::size_type));
// and actual string
file.write(orgs[i].name.data(), sizeof(sz));
file.write(reinterpret_cast<char*>(&orgs[i].paidTaxes), sizeof(float));
}
file.close();
}
and here is the code part where I'm reading this file back:
count = 0;
Organization org;
ifstream file;
file.open("orgs.bin", ios::binary);
while (file.good()) {
string::size_type sz;
file.read(reinterpret_cast<char*>(&org.id), sizeof(int));
file.read(reinterpret_cast<char*>(&sz), sizeof(string::size_type));
org.name.resize(sz);
file.read(&org.name[0], sz);
file.read(reinterpret_cast<char*>(&org.paidTaxes), sizeof(float));
count++;
}
As far as I understand, I need to run this loop to determine how many structs are stored in a file. When I run debugger, I successfully read id field, string size and actual string. However, I never get proper paidTaxes field (type of float) and subsequent loops return me junk.
thanks in advance!
The problem is in this line
file.write(orgs[i].name.data(), sizeof(sz));
Here is a typo, insted use
file.write(orgs[i].name.data(), sz);
You shoud write sz bytes, but not sizeof(sz). There is also one more problem with your code. You will read the last record twice, because file.good() will return true after the last record. You can read like that:
while (file.good()) {
string::size_type sz;
if( !file.read(reinterpret_cast<char*>(&org.id), sizeof(int)) )
break;
file.read(reinterpret_cast<char*>(&sz), sizeof(string::size_type));
org.name.resize(sz);
file.read(&org.name[0], sz);
file.read(reinterpret_cast<char*>(&org.paidTaxes), sizeof(float));
count++;
std::cout << org.id << " " << org.name << " " << org.paidTaxes << std::endl;
}

Read file and create integer array dynamically

This seems like such an easy task, but everything I've tried hasn't worked so far.
I have a file foo.txt:
3
3 4 2
Now I want to read this file, read the first line and instantiate an int array with the size of the number it read on the first line.
Then it should populate that array with the elements in the second line, which has the exact same amount of elements and noted in line one.
If we're going to give you example code, might as well show you the best way to do it:
std::ifstream datafile("foo.txt");
if (!datafile) {
std::cerr << "Could not open \'foo.txt\', make sure it is in the correct directory." << std::endl;
exit(-1);
}
int num_entries;
// this tests whether the number was gotten successfully
if (!(datafile >> num_entries)) {
std::cerr << "The first item in the file must be the number of entries." << std::endl;
exit(-1);
}
// here we range check the input... never trust that information from the user is reasonable!
if (num_entries < 0) {
std::cerr << "Number of entries cannot be negative." << std::endl;
exit(-2);
}
// here we allocate an array of the requested size.
// vector will take care of freeing the memory when we're done with it (the vector goes out of scope)
std::vector<int> ints(num_entries);
for( int i = 0; i < num_entries; ++i )
// again, we'll check if there was any problem reading the numbers
if (!(datafile >> ints[i])) {
std::cerr << "Error reading entry #" << i << std::endl;
exit(-3);
}
}
Demo (with small changes because I can't provide a file with the right name on ideone): http://ideone.com/0vzPPN
You need to use ifstream object just like you use cin
ifstream fin("foo.txt"); //open the file
if(!fin.fail()){
int count;
fin>>count; //read the count
int *Arr = new int[count];
for(int i=0;i<count;i++){ //read numbers
fin>>Arr[i];
}
//... do what you need ...
//... and finally ...
delete [] Arr;
}
If you open a file using input filestream you can simply do that:
std::ifstream file_txt("file.txt");
int number_count = 0;
file_txt >> number_count; // read '3' from first line
for (int number, i = 0; i < number_count; ++i) {
file_txt >> number; // read other numbers
// process number
}
Filestreams just like other standard streams (std::cin, std::cout) can apply formatting depending on type supplied to operator>> (in this case an int).
This apply to both input and output.
Alternatively, you could avoid the entire need to read in the size beforehand by simply loading it into a std::vector:
std::ifstream fin("myfile.txt");
std::vector<int> vec{std::istream_iterator<int>(fin), std::istream_iterator<int>()};
fin.close();
or, if you cannot use C++11 syntax:
std::ifstream fin("myfile.txt");
std::vector<int> vec;
std::copy(std::istream_iterator<int>(fin), std::istream_iterator<int>(), std::back_inserter(vec));
fin.close();

C++ checksum reading nonexistent newline

I am doing a very basic checksum on files by reading the input file into a character array, and then iterating over that array and adding each character into the checksum. The problem is that when I do this all of my checksums are 10 too high (10 is the ascii decimal value for the newline character).
How is it newline characters are being inserted into my code, when I know for a fact there is no newline character in my text? Even a single line text file gets a newline character added in!
#include <iostream>
#include <fstream>
int main () {
int fileLength = 0;
std::ifstream inputFile;
char charArray[10000];
int checkSumValue = 0;
// open file in binary
inputFile.open("/Path/To/File", std::ios::binary);
// get file length, then return to beginning of file
inputFile.seekg(0, std::ios_base::end);
fileLength = inputFile.tellg();
inputFile.seekg(0, std::ios_base::beg);
// read all data from file into char array
inputFile.read(charArray, fileLength);
// iterate over char array, adding ascii decimal value to checksum
for (int num = 0; num <= fileLength; num++) {
std::cout << "Checksum value before iteration " << num << " is "
<< checkSumValue << std::endl;
checkSumValue += static_cast<int>(charArray[num]);
}
// properly close out the input file
inputFile.close();
inputFile.clear(std::ios_base::goodbit);
std::cout << "The checksum value is: " << checkSumValue << std::endl;
std::cout << "The file length is: " << fileLength << std::endl;
return 0;
}
Your problem is here:
num <= fileLength
It should be:
num < fileLength
For example. If the length is 1. Then the only valid character is charArray[0]
Also note. Doing this:
inputFile.read(charArray, fileLength);
is dangerious as fileLength may be larger than the size of the array.
A better solution would be to use a vector (as it dynamically sizes)
std::vector<char> charArray(fileLength);
inputFile.read(&charArray[0], fileLength);
But do you really need to copy the data into an array? Why not just do the sum on the fly.
size_t checkSumValue = std::accumulate(std::istreambuf_iterator<char>(fileLength),
std::istreambuf_iterator<char>(),
size_t(0)
);
Martin was also correct - you should be (num < fileLength) in all cases.
The other possibility is that you created your file in an editor and it's artificially added a spurious newline for you. That's common. Try dumping your file in a hex editor. I just ran your program (with the <= removed) and it works fine.