I am making a c++ installer and I have appended both the file to extract and an 8 byte filesize of the file to extract within the program, to the executable. My program exits on a file read error, whats going wrong? To note I don't have any knowledge about c file managing, apart from what I've learned today. I am writing the file test_before.tar.gz, which is 161 bytes, the executable is 12335 bytes long and the filesize file is 8 bytes long, containing 0000161. What's wrong? Ask for more info if needed.
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int main(int argc, char* argv[])
{
cout << "Opening the executable as read-only!" << endl;
FILE *exeFile; // The executable file pointer
FILE *outFile; // The file to write pointer
// Check whether a file name was supplied
if(argc < 2)
{
cout << "Please enter the file to write!" << endl;
return 1;
}
// Open the executable as read-only
if((exeFile = fopen(argv[0], "rb")) == 0)
{
cout << "Error opening the executable!" << endl;
return 1;
}
cout << "Getting the executables size!" << endl;
// Get the files size
fseek(exeFile, 0, SEEK_END);
int size = ftell(exeFile);
cout << "Reading ofset!" << endl;
// Read the ofset bytes contained in the last 7-bytes
char filesize_char[9];
fseek(exeFile, -8, SEEK_END);
fgets(filesize_char, 9, exeFile);
// Convert
int filesize = atoi(filesize_char);
int ofset = (size - filesize) - 8;
cout << "The ofset size is " << ofset << " bytes!" << endl;
cout << "The file size is " << filesize << " bytes!" << endl;
cout << "Reading the file to extract!" << endl;
// Create the variable to contain the file and goto the ofset
char* contents = new char[filesize + 1];
fseek(exeFile, ofset, SEEK_SET);
// Read the file to extract
if(fread(contents, sizeof(char), filesize + 1, exeFile) != sizeof(contents))
{
// Error has occured
if(feof(exeFile)) {
cout << "Premature end of file!" << endl;
// Delete variables so they dont "leak"
fclose(exeFile);
delete[] contents;
return 1;
} else {
cout << "File read error!" << endl;
// Delete variables so they dont "leak"
fclose(exeFile);
delete[] contents;
return 1;
}
}
cout << "Writing the file to " << argv[1] << "!" << endl;
// Write the file to extract
if((outFile = fopen(argv[1], "wb")) == 0)
{
cout << "Error opening the file to write!" << endl;
// Delete variables so they dont "leak"
fclose(exeFile);
fclose(outFile);
delete[] contents;
return 1;
}
fwrite(contents, 1, sizeof(contents), outFile);
//delete variables
fclose(exeFile);
fclose(outFile);
delete[] contents;
return 0;
}
You don't need the while loop at all. After all, you already allocated the memory that would contain all your data. Move file pointer to the start of data fseek(exeFile, ofset, SEEK_SET), then use fread to read it as whole, and then use fwrite to write it into outFile.
You should open your exeFile and outFile with "rb" and "wb" flags otherwise your code would work reliably only with text data.
Related
I'd like to get some assistance regarding the following line of code.
for the function constructArray,I'm unable to run it as shows the !aFile as true message but I have no idea what's the error.
Really appreciative of your assistance
Also how do I create inFile fileName with .txt,i've tried indentation with +".txt" but due to the file type in the argument i'm unable to do so.
Compiler run image: https://imgur.com/a/wkGwL
using namespace std;
enum NumType {Odd, Even};
struct Number
{
int no;
NumType type;
int oddDigits;
int evenDigits;
int sumDigits;
int noDigits;
};
// Create inFile data file with certain number of integers which are randomly generated
void constructInfile (fstream& aFile, char fileName[]);
// Read data from infile txt file and transfer to array of numbers
int constructArray (fstream& aFile,const char fileName[], Number ran[]);
/*
void processArray (Number [ ], int);
// Transfer information from array and store into output file called outfile txt with specific information format
void arrayToOutfile (fstream&, char [ ], Number [ ], int);
*/
const int MAX = 50;
int main()
{
srand(time(NULL));
fstream aFile;
char fileName [MAX];
cout << "Enter designated file name to be created" << endl;
cin >> fileName;
constructInfile (aFile,fileName);
Number ran[MAX];
int recNo = constructArray(aFile,fileName,ran);
cout << recNo << " of records transferred" << endl;
}
// Create inFile data file with certain number of integers which are randomly generated
void constructInfile (fstream& aFile,char fileName[]){
aFile.open(fileName, ios::out);
if(aFile.fail()){
cout << "File open unsuccessful" << endl;
aFile.close();
exit(1);
}
cout << "Begin creation of " << fileName << " file" << endl << endl;
int size = rand()%51+50;
for(int a = 0;a < size;a++){
aFile << rand()%1000+1 << endl;
}
cout << fileName << " file successfully created" << endl;
}
// Read data from infile txt file and transfer to array of numbers
int constructArray (fstream& aFile,const char fileName[], Number ran[]){
aFile.open (fileName, ios::in);
if (!aFile)
{
cout << fileName << " failed to open" << endl;
aFile.close ();
return 0;
}
cout << "Begin from " << fileName << " to array" << endl;
int i = 0;
char tabKey;
while (aFile >> ran[i].no)
{
aFile.get (tabKey); // read and discard
i++;
}
aFile.close ();
cout << fileName << " to array done" << endl;
return i;
}
You are opened a file in ios::out mode and never closed it!
Again you are trying to open the same file in ios::in mode. How a file could be opened twice in 2 different mode without properly closing the fstream?
You need to close the filestream inside the function constructInfile()!!!
You have opened the file but forget to close it. At the end of both of the functions, close the aFile. That should solve the problem.
I have a function that uses zlib to unzip files from a zip file to memory, and then stores the resulting file content in a string object. The problem is that the string is now composed of the binary representation of the files, and I don't know how to read that string in blocks like with
ifstream::read():
file.read((char*)result, sizeof(int));
It's necessary that I read the data in block by block, but I haven't found any process to do that from a string variable.
This is the function that returns the file content as a string (I can change the function if there's a better approach):
string externalresourcemanager::getFileInsideZip(string zipFile, string fileInZip) {
int err = UNZ_OK; // error status
uInt size_buf = WRITEBUFFERSIZE; // byte size of buffer to store raw csv data
void* buf; // the buffer
string sout; // output strings
char filename_inzip[256]; // for unzGetCurrentFileInfo
unz_file_info file_info; // for unzGetCurrentFileInfo
unzFile uf = unzOpen(zipFile.c_str()); // open zipfile stream
if (uf==NULL) {
cerr << "Cannot open " << zipFile << endl;
return sout;
} // file is open
if ( unzLocateFile(uf,fileInZip.c_str(),1) ) { // try to locate file inside zip
// second argument of unzLocateFile: 1 = case sensitive, 0 = case-insensitive
cerr << "File " << fileInZip << " not found in " << zipFile << endl;
return sout;
} // file inside zip found
if (unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0)) {
cerr << "Error " << err << " with zipfile " << zipFile << " in unzGetCurrentFileInfo." << endl;
return sout;
} // obtained the necessary details about file inside zip
buf = (void*)malloc(size_buf); // setup buffer
if (buf==NULL) {
cerr << "Error allocating memory for read buffer" << endl;
return sout;
} // buffer ready
err = unzOpenCurrentFilePassword(uf,NULL); // Open the file inside the zip (password = NULL)
if (err!=UNZ_OK) {
cerr << "Error " << err << " with zipfile " << zipFile << " in unzOpenCurrentFilePassword." << endl;
return sout;
} // file inside the zip is open
// Copy contents of the file inside the zip to the buffer
cout << "Extracting: " << filename_inzip << " from " << zipFile << endl;
do {
err = unzReadCurrentFile(uf,buf,size_buf);
if (err<0) {
cerr << "Error " << err << " with zipfile " << zipFile << " in unzReadCurrentFile" << endl;
sout = ""; // empty output string
break;
}
// copy the buffer to a string
if (err>0) for (int i = 0; i < (int) err; i++) sout.push_back( *(((char*)buf)+i) );
} while (err>0);
err = unzCloseCurrentFile (uf); // close the zipfile
if (err!=UNZ_OK) {
cerr << "Error " << err << " with zipfile " << zipFile << " in unzCloseCurrentFile" << endl;
sout = ""; // empty output string
}
free(buf); // free up buffer memory
return sout;
}
Thank you!
EDIT:
im trying your solution (john) but i get an empty result. Here is an example code to explain the problem:
int store = 1024;
string result = "";
ofstream file("test.txt", std::ios::binary);
//save a few data
file.write(reinterpret_cast<const char*>(&store), sizeof(int));
file.write(reinterpret_cast<const char*>(&store), sizeof(int));
file.close();
//remember that i receive the "string binary data" from the getFileInsideZip() function (here i simulating the problem and store the binary data in a string only for explanation purpose)
ifstream newFile("test.txt", std::ios::binary);
newFile.seekg(0, std::ios::end);
int length = newFile.tellg();
newFile.seekg(0, std::ios::beg);
char* begin = &*result.begin();
//here i have a binary file in a string variable
newFile.read(begin, length);
newFile.close();
cout << "result:" << result << "\n";
//now im trying to get the first int from this binary data
int stored = 0;
std::istringstream bin(result);
bin.read((char*)&stored, sizeof(int));
cout << "stored: " << stored << "\n";
//but the output is:
//result -> empty (initial value)
//stored: 0 -> (initial value)
any suggestion?
I have the following code:
char*
Sender::PrepareData(char* filename, unsigned long long int bytesToTransfer)
{
FILE* dataFile = fopen(filename, "rb");
if (dataFile==NULL) {fputs ("File error",stderr); exit (1);}
cout << "File Open: " << filename << endl;
char* theData;
size_t bytesRead = fread(&theData, 1, bytesToTransfer, dataFile);
if (bytesRead != bytesToTransfer) {fputs ("Reading error",stderr); exit (3);}
cout << "Data Read -- Num Bytes: " << bytesRead << endl;
cout << "Data to Send: " << *theData << endl;
return theData;
}
When this method gets hit, my output is:
File Open: t.bin
Data Read -- Num Bytes: 10
Segmentation fault (core dumped)
My t.bin file contains the following:
This is a test.
98172398172837129837
alsjdf89u32ijofiou2
TEST TEST...
!!## TESTING TEST!! ###(DLKAJ)
When I run through gdb, the segfault output is:
File Open: t.bin Data Read -- Num Bytes: 10
Program received signal SIGSEGV, Segmentation fault. 0x00000000004015e2 in Sender::PrepareData (this=0x603010, filename=0x7fffffffe363 "t.bin", bytesToTransfer=10)
at sender.cpp:98 98 cout << "Data to Send: " << *theData << endl;
Can someone tell me what I'm doing wrong?
You need a buffer, theData is just a pointer.
Something like
char theData[1000];
size_t bytesRead = fread(theData, 1, bytesToTransfer, dataFile);
might work depending on the maximum value of bytesToTransfer
If you are sure you are reading a string you might also need to terminate theData before writing to cout,
theData[bytesRead] = '\0';
You'll need to allocate your buffer on the heap if you want to return it.
It'd be a lot easier to do something like
std::vector<char>
PrepareData(char* filename, unsigned long long int bytesToTransfer)
{
std::ifstream file(filename, file.binary);
if (!file) {
std::cerr << "File error";
exit(1);
}
std::cout << "File Open: " << filename << '\n';
std::vector<char> data(bytesToTransfer + 1);
file.read(data.data(), bytesToTransfer);
data.back() = '\0';
if (!file) {
std::cerr << "Reading error";
exit(3);
}
std::cout << "Data Read -- Num Bytes: " << bytesToTransfer << '\n';
std::cout << "Data to Send: " << data.data() << '\n';
return data;
}
Then again if all you are doing is reading chars from a file you should probably consider using strings.
The code below does not work under Windows and GNU C++, VS10,VS12, Intel C++ 14.0. The code below does work under Linux and GNU C++ 4.7, 4.8, Intel C++ 14, Open64 5.0.Replacing DIMEN with DIMEN-256 in the inner test for-loop ... works!? Any idea?
//============================//
// Read and Write binary file //
// using buffers //
//============================//
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
using namespace std;
int main()
{
// 1. variables and parameters
const long int DIMEN = static_cast<long int>(pow(10.0,8.0));
const long int I_DO_MAX = 100;
const string fileName = "my_file.bin";
ofstream fileOUT;
ifstream fileIN;
double* myArrayAlpha = new double [DIMEN];
double* myArrayBeta = new double [DIMEN];
long int i;
long int j;
// 2. build the array with some data
cout << " 1 --> Build the array with some data" << endl;
for (i = 0; i < DIMEN; i++)
{ myArrayAlpha[i] = static_cast<double>(i); }
for (i = 0; i < I_DO_MAX; i++)
{
// 3. open the file stream
cout << "-------------->>> " << i << endl;
cout << " 2 --> Open the file stream" << endl;
fileOUT.open(fileName.c_str(), ios::out | ios::binary | ios::trunc);
fileIN.open(fileName.c_str(), ios::in | ios::binary);
// 4. test if the file stream is opened
cout << " 3 --> Test if the file stream is opened with success" << endl;
if (!fileOUT.is_open())
{ cout << "Error! The output file stream is not opened. Exit."
<< endl; return -1; }
if (!fileIN.is_open())
{ cout << "Error! The input file stream is not opened. Exit."
<< endl; return -1; }
// 5. write the contents of myArrayAlpha[] to a file
cout << " 4 --> Write and then Read to the file" << endl;
fileIN.seekg(0, fileIN.beg);
fileOUT.seekp(0);
fileOUT.write(reinterpret_cast<char*>(&myArrayAlpha[0]),
DIMEN * sizeof(double));
fileIN.read(reinterpret_cast<char*>(&myArrayBeta[0]),
DIMEN * sizeof(double));
// 6. test that I am writting and reading correctly
for (j = 0; j < DIMEN; j++) // replace DIMEN
{ // with DIMEN-256 to work under Windows
if (myArrayAlpha[j] != myArrayBeta[j])
{ cout << myArrayAlpha[j] << endl;
cout << myArrayBeta[j] << endl;
cout << "j = " << j << endl;
cout << "Error!"; return -1; }
}
cout << " 5 --> Read and Write with success" << endl;
cout << " 6 --> Close the I/O streams" << endl;
// 7. close the file stream
fileIN.close();
fileOUT.close();
}
// 8. free up the RAM
delete [] myArrayAlpha;
delete [] myArrayBeta;
return 0;
}
The problem is that your data is not being flushed to the external sequence after the write call, so it is still positioned in the internal buffer. Add this line after write():
fileOUT << std::flush;
this code seems to work under Windows (with unexpected results) and Ubuntu. But when I run it under FreeBSD 9.0 AMD 64 it causes the system to freeze. I get error messages like this:
ahcich0: Timeout on slot 28 port 0
Does anybody know what the problem could be?
Thanks.
#include <cmath>
#include <cstdlib>
#include <sys/time.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
const string FILENAME = "testfile";
const string COPYNAME = "copy";
const int FILES = 5;
const int SIZE_MULTIPLIER = 6;
const int BUFFER_SIZE = pow(2.0, 16);
time_t times[2][FILES];
srand (time(NULL));
// create test files
for (int i = 1; i < FILES + 1; i++){
ofstream os;
string filename(FILENAME);
filename += (char)i + 48;
os.open(filename.c_str(), ios::binary);
if (os.is_open()){
cout << "Writing file " << i << " of " << FILES;
long filesize =pow(2.0, i * SIZE_MULTIPLIER);
cout << " (" << filesize << " bytes)" << endl;
while(filesize--){
os << (char)(rand() % 256);
}
cout << os.tellp() << " bytes written.\n";
os.close();
}else{
cerr << "Could not create file " << filename;
cerr << endl;
}
}
// copy the files
timeval tv;
time_t start;
char buffer[BUFFER_SIZE];
char ci;
for (int i = 0; i < FILES; i++){
ci = (char)i + 49;
string filename(FILENAME);
filename += ci;
string copyname("c");
copyname += COPYNAME;
copyname += ci;
cout << "Copying file " << filename.c_str() << endl;
cout << "the c way: ";
cout.flush();
start = time(NULL);
FILE *pFile = fopen(filename.c_str(), "rb");
FILE *pCopy = fopen(copyname.c_str(), "wb");
if (!(pFile == NULL || pCopy == NULL)){
do{
int bytesRead = fread(
buffer, 1, BUFFER_SIZE, pFile);
fwrite(buffer, 1, bytesRead, pCopy);
}while(!feof(pFile));
fclose(pFile);
fclose(pCopy);
cout << " Done.\n";
}else{
cerr << "Could not open either " << filename;
cerr << " or " << copyname << endl;
}
times[0][i] = time(NULL) - start;
remove(copyname.c_str());
copyname = "cpp";
copyname += COPYNAME;
copyname += ci;
cout << "the c++ way: ";
cout.flush();
start = time(NULL);
ifstream in;
in.open(filename.c_str(), ios::binary);
in.rdbuf()->pubsetbuf(buffer, BUFFER_SIZE);
ofstream out;
out.open(copyname.c_str(), ios::binary);
char copyBuffer[BUFFER_SIZE];
out.rdbuf()->pubsetbuf(copyBuffer, BUFFER_SIZE);
if (in.is_open() && out.is_open()){
out << in.rdbuf();
in.close();
out.close();
cout << " Done.\n";
}else{
cerr << "Could not open either " << filename;
cerr << " or " << copyname << endl;
}
times[1][i] = time(NULL) - start ;
remove(copyname.c_str());
}
cout << "Summary:\n";
cout << "\tc\tc++\n";
for (int i = 0; i < FILES; i++){
ci = (char)i + 49;
cout << "copy" << ci << "\t" << times[0][i];
cout << "\t" << times[1][i] << endl;
}
return 0;
}
After changing FILES to 4 (because it takes very long otherwise), your program ran just fine here:
Writing file 1 of 4 (64 bytes)
64 bytes written.
Writing file 2 of 4 (4096 bytes)
4096 bytes written.
Writing file 3 of 4 (262144 bytes)
262144 bytes written.
Writing file 4 of 4 (16777216 bytes)
16777216 bytes written.
Copying file testfile1
the c way: Done.
the c++ way: Done.
Copying file testfile2
the c way: Done.
the c++ way: Done.
Copying file testfile3
the c way: Done.
the c++ way: Done.
Copying file testfile4
the c way: Done.
the c++ way: Done.
Summary:
c c++
copy1 0 0
copy2 0 0
copy3 0 0
copy4 0 0
(FreeBSD 9.0-RELEASE-p3 amd64, compiled with clang++)
There could've been a bug in the achi-driver in 9.0, that showed up under heavy load. Or, it could've been a buggy controller, that was failing under the same load -- and not failing under other OSes, because they weren't taxing it as much.
Is this still a problem with FreeBSD 9.2?
As for your program, you ought to check not just for feof(), but also for ferror() in your read/write loop. Further, in my opinion, such read/write loops are a thing from the past. These days, when size_t and offset_t are of the same width (64-bit platforms), you ought to simply mmap() your source file and fwrite it into destination in one go. Look, ma, no loop!