Visual C++ - Writing doubles and integers to binary files - c++

I'm reading info from one text file, then outputting it to a bin file. I got the name to write properly, but the integer and doubles do not work properly. Disregard the system("pause"), they are so I can check my outputs.
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
struct SaleSlip{
char name[20];
int prodID;
double value;
};
void main(){
fstream slips, binslip;
SaleSlip sales[17];
binslip.open("SaleSlips.bin", ios::out | ios::binary | ios::trunc);
slips.open("SaleSlips.txt", ios::in);
if(slips.eof()){
cout << "Cannot open file - SaleSlips.txt"<< endl;
system("pause");
exit(2);
}
int i = 0;
while(!slips.eof()){
slips >> sales[i].name;
slips.ignore(80, ' ');
slips >> sales[i].prodID;
slips.ignore(80, ' ');
slips >> sales[i].value;
slips.ignore(80, '\n');
cout << sales[i].name;
cout << sales[i].prodID;
cout << sales[i].value << endl;
binslip.write((const char *)&sales[i].name, sizeof(sales[i].name));
binslip.write((const char *)&sales[i].prodID, sizeof(sales[i].prodID));
binslip.write((const char *)&sales[i].value, sizeof(sales[i].value));
i++;
}
slips.close();
binslip.close();
system("pause");
}
SaleSlips.txt has one line per item of the array so "Ryan 2 1400.52" is one line. Ryan is interpreted correctly, but when I read back in from the binary it isn't right, nor is it when I check it in textpad.

binslip.write is an UnformattedOutputFunction. There is formatted and unformatted input/output. When you use unformatted output, you are writing raw bytes to the file. There is no interpretation as to what the bytes actually represent.
When you wrote to SalesSlips.bin, you wrote characters followed by numbers (rather, what you thought would be numbers). When you tried to examine SalesSlips.bin, the computer read text characters, and was expecting the rest of the file to be text, but it wasn't. Numbers are represented differently then characters. Even if you were to just deal with numbers alone, integral and floating point values are represented differently in binary. It may be a bit much for you to absorb right now, but the gist of it is you need to use a FormattedOutputFunction. This will perform the internal conversions necessary for you to output your data properly.
Imagine you have a number, lets say, 50. That number is meaningless without units that describe what 50 represents. 50 seconds? 50 feet? 50 states? You can think of binslip.write as just writing 50, and when you try to read the file later, all you see is 50, and you don't understand what it was supposed to represent, which is why it looks like gibberish to you.
To fix your code you need to replace the binslip.write lines with this:
binslip << sales[i].name << sales[i].prodID << sales[i].value;
In this case, operator<< behaves as a FormattedOutputFunction, so when you write SaleSlips.bin, you can open it in a text editor and view Ryan 2 1400.52 like you expected.

While reading and writing in BINARY mode, you are not expected to read the data in any text editor. It simply serves no purpose. If you want to read it in a text editor, recommendation is to use NORMAL mode.
Anyways, if you don't bother about viewing your BINARY data in a text editor, you could use the following lines to Read and write binary file:
while(!slips.eof())
{
slips >> sales[i].name;
slips.ignore(80, ' ');
slips >> sales[i].prodID;
slips.ignore(80, ' ');
slips >> sales[i].value;
slips.ignore(80, '\n');
cout << sales[i].name;
cout << sales[i].prodID;
cout << sales[i].value << endl;
//// WRITE THE STRUCTURE AT ONCE
binslip.write((const char *)&sales[i], sizeof(sales[i]));
i++;
}
binslip.flush();
binslip.close();
//// OPEN UP THE FILE IN READ MODE
binslip.open("SaleSlips.bin", ios::in | ios::binary );
SaleSlip sale;
//// READ UP THE COMPLETE STRUCTURE
binslip.read((char *)&sale, sizeof(sale));
//// PRINT IT.
cout<<sale.name;
cout<<sale.prodID;
cout<<sale.value;

Related

why does removing blank space while writing into file fails reading files?

I'm learning C++, I find problems reading a file if I didn't add blank space while writing into it.
Plus, the file I wrote into doesn't contain blank space as intended.(I opened it with notepad++)
Btw, I'm using code::blocks17.12.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{ ofstream out("file1");
int i;
ifstream in;
if(!out){cerr << "create file error!\n"; return 1;}
for(i = 1; i<=10; ++i) out << i <<' ';
/*if I remove (<<' ') here, nothing was pirnted on screen. */
out.close();
in.open("file1");
if(!in){cerr << "open file error!\n"; return 1;}
while(in >> i) cout<< i << ' ';
in.close();
return 0;
}
If you write 1, 3 and 8 to a file without spaces then you get 138 how do you now want to figure out that it was not 138 that was written?
The input stream needs some kind of indication of how the numbers are separated.
If you want to know why they decided that writing a number does not automatically add a space, that is because it is not always the desired behavior.
And as Martin Heralecký correctly mentions. in >> i does not read in anything because without spaces 12345678910 is written to the file, which is most certainly out of the range of an int only your setup.
The actual size of an int is platform-dependent but you should not expect that it can store numbers larger than 2147483647.
More details about the Fundamental types: Properties

.size() string manipulation not reading actual length/size of characters c++

I am trying to read in an essay from a file which I then need to change each beginning letter of a sentence to an upper case letter and then send the corrected essay back to a file called correct.txt. The essay is stored in essay.txt.
So far I am just working with understanding the conversions from files to string in order for me to proceed with the rest of the question. So far, I have a string variable which which holds the essay with the words separated by a single space. I noticed that when I was trying to work with the size of my new string, it was not giving me the correct answer and I cannot figure out why. If you have any suggestions on how I can get it to notice the correct amount of characters, I would really appreciate it.
One more question while you're here, I know that moving forward, in order to change the beginning letters of the sentence to upper case, I need to first find the periods. Once I have this position, I can use pos+2 (including the preceding whitespace after the period) for the character that needs to become upper case. Is this the correct way of going about this and do you have any other tips on how to go forward with this?
Here is my code so far:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
int main(){
//declaring variables and creating objects
ifstream inputFile;
ofstream outputFile;
char inputFileName[20], outFileName[20];
cout << "Enter name of the file you want to open: " << endl;
cin >> inputFileName;
inputFile.open(inputFileName);
if (inputFile.fail()) {
cout << "Input file opening failed.\n";
exit(1);
}
cout << "Enter name of the file you want to send the output to: " << endl;
cin >> outFileName;
outputFile.open(outFileName);
if (outputFile.fail()) {
cout << "Output file opening failed.\n";
exit(1);
}
//while the file is open, it sends the contents to the string variable "essay"
string essay;
inputFile >> essay;
while (!inputFile.eof()) {
cout << essay << " ";
inputFile >> essay;
}
//this is to check for the correct size of the string "essay" before moving on to the rest of the code
int size = essay.size();
cout << size << endl;
return 0;
}
Your understanding of how the input stream works is incorrect.
The core of your code is this loop:
string essay;
inputFile >> essay;
while (!inputFile.eof()) {
cout << essay << " ";
inputFile >> essay;
}
What this does is that it reads the first word into essay, then, as long as the eof marker is not set on the stream it echoes back the word just read, and then reads another word, overwriting the previous one.
Here's the correct code. Note that checking for eof in a loop condition is a bad idea, because it doesn't quite do what you want, and would also get you stuck in an infinite loop if the stream instead entered an error condition.
string word;
while (inputFile >> word) { // read a word and stop if this fails for any reason
essay += word;
essay += " ";
}
Though I'm not sure why you read the file word by word instead of all at once.
Also, I feel the need to repeat what M.M. said in a comment: your use of raw character arrays on input is unsafe and unnecessary. Just use string. You need to then write inputFile.open(inputFileName.c_str()) unless your standard library is new enough to have the string overloads of these functions, but that is fine. The other way of doing it is dangerous and a very bad habit to get into.
Try include cstring on top of string as well.
String is considered char array which is a more 'unique' way of storing data. You can try the code listed below.
int size = essay.length();

Not able to read whole file

I'm making a C++ program to be able to open a .bmp image and then being able to put it in a 2D array. Right now i have the code like this:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include "Image.h"
using namespace std;
struct colour{
int red;
int green;
int blue;
};
Image::Image(string location){
fstream stream;
string tempStr;
stringstream strstr;
stream.open(location);
string completeStr;
while(!stream.eof()){
getline(stream, tempStr);
completeStr.append(tempStr);
}
cout << endl << completeStr;
Image::length = completeStr[0x13]*256 + completeStr[0x12];
Image::width = completeStr[0x17]*256 + completeStr[0x16];
cout << Image::length;
cout << Image::width;
cout << completeStr.length();
int hexInt;
int x = 0x36;
while(x < completeStr.length()){
strstr << noskipws << completeStr[x];
cout << x << ": ";
hexInt = strstr.get();
cout << hex << hexInt << " ";
if((x + 1)%3 == 0){
cout << endl;
}
x++;
}
}
Now if i run this on my test file of 256x256 it will print fine, until it reaches 0x36E where it gives an error / doesn't go further. This happens because the completeStr string doesn't recieve all the data that is in the bmp file. Why isn't able to read all the lines in the bmp file?
There are a number of problems with your code. The principal
one (and probably the reason for your problem) is that you are
opening the file in text mode. Technically, this means that if
the file contains anything but printable characters and a few
specific control characters (like '\t'), you have undefined
behavior. In practice, under Windows, this means that sequences
of 0x0D, 0x0A will be converted into a single '\n', and that
a 0x1A will be interpreted as the end of the file. Not really
what one wants when reading binary data. You should open the
stream in binary mode (std::ios_base::binary).
Not a serious error, but you shouldn't really use an fstream
if you are only going to read the file. In fact, using an
fstream should be very rare: you should use either ifstream
or ofstream. The same thing holds for stringstream (but
I don't see any role for stringstream when reading a binary
file).
Also (and this is a real error), you are using the results of
getline without checking whether is succeeded. The usual
idiom for reading lines would be:
while ( std::getline( source, ling ) ) ...
But like stringstream, you don't want to use getline on
a binary stream; it will remove all of the '\n' (which have
already been mapped from CRLF).
If you want all of the data in memory, the simplest solution is
something like:
std::ifstream source( location.c_str(), std::ios_base::binary );
if ( !source.is_open() ) {
// error handling...
}
std::vector<char> image( (std::istreambuf_iterator<char>( source ) ),
(std::istreambuf_iterator<char>()) );
std::getline reads in a line of text.
It's not useful for a binary file.
Open the file in binary mode and use unformatted input operations (like read).

Reading text file into C++

I was writing a program which could read inputs directly from a text
file into a C++ file. However, the contents of the file come in
different formats, for example time. The input file looks like this:
Time(1) Price(1)
8:56:18 1250.00
9:00:25 1250.25
9:21:36 1250.50
9:23:32 1249.75
Time(2)
8:55:28
9:02:14
9:20:23
9:21:37
Price(2)
1680.50
1681.00
1680.50
1681.50
My program to read the file is as follows:
int main()
{
string file;
cout << "Enter a file name to read input: ";
cin >> file;
ifstream file_name(file.c_str());
while(!file_name.eof())
{
double input;
file_name >> input;
cout << input << endl;
}
}
But when I executed the program, I get stuck in an infinite loop and
all I see are 0s written on the screen. Is this being caused due the
formatting of the time?
The default behavior of file_name >> input is type safe therefore file_name byte offset pointer never increments for inputs like Time(1) or 8:56:18. You may use string input; instead of double input; to retrieve the values, then later you may check their types by using following standard c library.
#include <cstdlib>
.
.
.
atof()
atoi()
.
Here is the documentation.

Trying to read a text file, Encrypt it with XOR, then Write it to a new text file

I am trying to design a program that will open any text file, read it into a string, encrypt the string with XOR, and write the string to a new text file. The code below works, but generates multiple "system beeps".
My wild guess is that I am not handling whitespaces correctly? I'm not sure. Any ideas what I'm doing wrong?
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ifstream inFile;
ofstream outFile;
// Define variables
string fileName,
key = "seacrest out";
// Input
cout << "Please enter the name of the file you wish to encrypt: ";
cin >> fileName;
cout << endl;
inFile.open(fileName, ios::in | ios::binary);
string str((istreambuf_iterator<char>(inFile)), istreambuf_iterator<char>()); // Reads a text file into a single string.
inFile.close();
cout << "The file has been read into memory as follows:" << endl;
cout << str << endl;
system("pause");
// Encryption
cout << "The file has been encrypted as follows:" << endl;
for (unsigned x = 0; x < str.size(); x++) // Steps through the characters of the string.
str[x] ^= key[x % key.size()]; // Cycles through a multi-character encryption key, and encrypts the character using an XOR bitwise encryption.
cout << str << endl; // This code works, but I get system beeps. Something is still wrong.
// Write Encrypted File
cout << "Please enter the file name to save the encrypted file under: ";
cin >> fileName;
cout << endl;
outFile.open(fileName, ios::out | ios::binary);
outFile.write(str.c_str(), str.size()); // Writes the string to the binary file by first converting it to a C-String using the .c_str member function.
system("pause");
return 0;
}
those beep sounds you heard are bytes equal to 0x07 in your file. you can get rid of this problem simply by not printing the contents of a binary file in the console.
kudos for trying to do it yourself.
the problem is you are not handling some characters carefully for eg white spaces may try to print out
char d=(char)(7);
printf("%c\n",d);
which is called a bell character.
here is an easy implementation for XOR encryption but i recommend to write your own version
http://programmingconsole.blogspot.in/2013/10/xor-encryption-for-alphabets.html
When you xor bytes with some random key, you're going to get some unusual byte sequences. These byte sequences happen to correspond to some unprintable characters that you can use to make the console beep by sending them to the console.
If you remove the line
cout << str << endl;
You'll find that your console won't beep any more since you're not printing the erroneous byte sequences that the console is interpreting as commands to beep.
If your console is set to ASCII mode (which I assume it is since you have system("PAUSE") which indicates you're on Windows where the console is not Unicode unless you set it explicitly IIRC) then those unprintable characters are all bytes less than 0x1F and the byte 0x7F, and the character that causes the console to beep is 0x7 (called "bell").
tl;dr
You get some 0x7 bytes in your encrypted data that cause the console to beep when printed. Remove cout << str << endl; to fix it.