need help converting c to c++ (simple error but cant fix) - c++

I have a c++ homework. The homework is asking to convert a c program to c++.
Below is the question:
You are requested to convert the following C function into a C++
function and then embed it into a complete program and test it. Note
that this function copies a binary file of integers and not a text
file. The program must accept the arguments (the file to copy and the
file to be copied to) from the command line.
/* ==================== cpyFile =====================
This function copies the contents of a binary file
of integers to a second file.
Pre fp1 is file pointer to open read file
fp2 is file pointer to open write file
Post file copied
Return 1 is successful or zero if error
*/
int cpyFile (FILE *fp1, FILE *fp2)
{
/* Local Definitions */
int data;
/* Statements */
fseek (fp1, 0, SEEK_END);
if (!ftell (fp1))
{
printf ("\n\acpyFile Error : file empty\n\n");
return 0;
} /* if open error */
if (fseek (fp1, 0, SEEK_SET))
return 0;
if (fseek (fp2, 0, SEEK_SET))
return 0;
while (fread (&data, sizeof (int), 1, fp1))
fwrite (&data, sizeof (int), 1, fp2);
return 1;
} /* cpyFile */
I did my best and managed to convert it, but unfortunately when I'm using it , the file that I get after the copy is empty. Below is my answer:
#include <fstream>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
if(argc!=3)
{cerr<<"invalid number of arguments. must be 3."<<endl;exit(1);}
fstream fp1(argv[1],ios::in);
if(!fp1)+{cerr<<argv[1]<<" could not be opened"<<endl;exit(1);}
fstream fp2(argv[2],ios::out);
if(!fp2)+{cerr<<"file could not be found."<<endl;exit(1);}
int data;
fp1.seekg (0,ios::end);
if (!fp1.tellg ())
{
cout<<"\n\acpyFile Error : file empty\n\n";
return 0;
} /* if open error */
if (fp1.seekg (0, ios::beg))
return 0;
if (fp2.seekg (0, ios::beg))
return 0;
while (fp1.read (reinterpret_cast<char*>(&data), sizeof (int)))
{
fp2.seekp(0);
fp2.write (reinterpret_cast<char*>(&data), sizeof (int));
}
return 1;
}
I did my best and everything is working fine, except that when I copy a binary file, the file that i get is empty and I have no idea why.

You need to open the file in binary mode, as others have said, by doing
fstream fp1(argv[1], ios::in | ios::binary); // combine ios::in with ios::binary
fstream fp2(argv[2], ios::out | ios::binary); // combine ios::out with ios::binary
Or you can make them ifstream (in file stream for reading only) and ofstream (out file stream, for writing only) and remove the ios::in and ios::out because ifstream implies ios::in and ofstream implies ios::out:
ifstream fp1(argv[1], ios::binary);
ofstream fp2(argv[2], ios::binary);
You need to do this because if you don't, the file will be translated when you read from or write to it for things like turning line endings from \r\n or \r to just \n, etc, which will mess up your binary data which may happen to have those bytes in them.
This:
if (fp1.seekg (0, ios::beg))
return 0;
if (fp2.seekg (0, ios::beg))
return 0;
Will always make your code return because seekg returns the object you call it on. It's not the equivalent of fseek in this regard because fseek returns 0 on success. So you never get to the while loop. Take those out of the if statements so that it looks like this:
fp1.seekg(0, ios::beg);
fp2.seekg(0, ios::beg);
Or if you have to have the checking, you want to do
if (!fp1.seekg (0, ios::beg)) // notice the added !
return 0;
if (!fp2.seekg (0, ios::beg)) // notice the added !
return 0;
Also, this (inside the while):
fp2.seekp(0);
Is setting the point you are going to write to to the beginning of the file. So you'll never write anything but at the beginning of the file. Just remove that line completely.
Also, you have a return inside the loop which makes it return on the first iteration. Move the return 1; outside the loop so you only return after the loop is finished. Nevermind that, misread due to the unusual brace style.

Every time you read a new data block from fp1, you rewind fp2 to the beginning of the stream, essentially discarding what you have already written to fp2. Try moving fp2.seekp(0) out of your main loop.

You have a few problems. I'd start by fixing this bit:
if (fp1.seekg (0, ios::beg))
return 0;
if (fp2.seekg (0, ios::beg))
return 0;
The seekg method returns a reference to the istream it's called on, so the above is equivalent to this:
fp1.seekg (0, ios::beg);
if (fp1) // i.e., if fp1 is in a valid state (as opposed to e.g. end-of-file)
return 0;
fp2.seekg (0, ios::beg);
if (fp2) // i.e., if fp2 is in a valid state (as opposed to e.g. end-of-file)
return 0;
which is obviously not what you want.
To debug your code, you can use statements like std::cout << "Got to line " << __LINE__ << std::endl; to figure out which parts of the program are actually being run. That would have found the above problem pretty quickly.

Binary files need to be opened specifically in binary mode, so where you have fstream fp1(argv[1],ios::in); you should also add an ios::binary to it like so: fstream fp1(argv[1], ios::in | ios::binary);

In the C++ code you are seeking to the beginning of the output file before writing each number, and therefore the output file will be at most 2 bytes long.

Related

C++ file stream for reading/writing

I need to open a file for both reading/writing using fstream and read each character then write that character back to the file. for example i have this code.
fstream in("test.txt",ios::in | ios::out);
if(!in)
cout<<"error...";
else
{
char ch;
in.seekg(0,ios::end);
int end=in.tellg();//get the length
in.seekg(0);//get back to the start
for(int i=0;i<end;i++)
{
//in.seekg(in.tellg());//if i uncomment this the code will work
if(!in.get(ch).fail())//read a character
{
in.seekp(static_cast<int>(in.tellg())-1);//move the pointer back to the previously read position,so i could write on it
if(in.put(ch).fail())//write back,this also move position to the next character to be read/write
break;//break on error
}
}
}
I have a file named "test.txt" which contains "ABCD". As i understand it both put() and get() methods of the stream object move the file pointer forward(i see that by getting the return value of tellg() or tellp() functions after each get() or put() method call). My question is when i comment out the code that will seek the stream pointer to "where it is now"(in.seekg(in.tellg()), the code will result incorrect results. I don't understand why this is since tellg() is showing the correct position of the character to be read next.what is the purpose of explicitly seeking to it? I am using visual studio 2005.
The incorrect result is it writes to the file "ABBB" instead of "ABCD".
The output buffer has to be flushed when switching between write and read.
fstream in("test.txt",ios::in | ios::out);
if(!in)
cout<<"error...";
else
{
char ch;
in.seekg(0,ios::end);
int end=in.tellg();//get the length
in.seekg(0);//get back to the start
for(int i=0;i<end;i++)
{
if(!in.get(ch).fail())//read a character
{
in.seekp(static_cast<int>(in.tellg())-1);//move the pointer back to the previously read position,so i could write on it
if(in.put(ch).fail())//write back,this also move position to the next character to be read/write
break;//break on error
in.flush();
}
}
}

How to detect if a file empty or not in c++? [duplicate]

Is there an easy way to check if a file is empty. Like if you are passing a file to a function and you realize it's empty, then you close it right away? Thanks.
Edit, I tried using the fseek method, but I get an error saying 'cannot convert ifstream to FILE *'.
My function's parameter is
myFunction(ifstream &inFile)
Perhaps something akin to:
bool is_empty(std::ifstream& pFile)
{
return pFile.peek() == std::ifstream::traits_type::eof();
}
Short and sweet.
With concerns to your error, the other answers use C-style file access, where you get a FILE* with specific functions.
Contrarily, you and I are working with C++ streams, and as such cannot use those functions. The above code works in a simple manner: peek() will peek at the stream and return, without removing, the next character. If it reaches the end of file, it returns eof(). Ergo, we just peek() at the stream and see if it's eof(), since an empty file has nothing to peek at.
Note, this also returns true if the file never opened in the first place, which should work in your case. If you don't want that:
std::ifstream file("filename");
if (!file)
{
// file is not open
}
if (is_empty(file))
{
// file is empty
}
// file is open and not empty
Ok, so this piece of code should work for you. I changed the names to match your parameter.
inFile.seekg(0, ios::end);
if (inFile.tellg() == 0) {
// ...do something with empty file...
}
Seek to the end of the file and check the position:
fseek(fileDescriptor, 0, SEEK_END);
if (ftell(fileDescriptor) == 0) {
// file is empty...
} else {
// file is not empty, go back to the beginning:
fseek(fileDescriptor, 0, SEEK_SET);
}
If you don't have the file open already, just use the fstat function and check the file size directly.
C++17 solution:
#include <filesystem>
const auto filepath = <path to file> (as a std::string or std::filesystem::path)
auto isEmpty = (std::filesystem::file_size(filepath) == 0);
Assumes you have the filepath location stored, I don't think you can extract a filepath from an std::ifstream object.
when the file is empty the tellg will give you value 0 if its empty so focus on that and it is the simplest way to find an empty file, if you just create the file it will give you -1.
outfile.seekg(0,ios::end);
if(file.tellg()<1){
//empty
}else{
file.clear(); // clear all flags(eof)
file.seekg(0,ios::beg);//reset to front
//not empty
}
If your use case offer the possibility to check for emptiness before opening the file,C++17 provides you is_empty
#include <filesystem>
if (!std::filesystem::is_empty("path.txt")) {
///Open and use the file
}
char ch;
FILE *f = fopen("file.txt", "r");
if(fscanf(f,"%c",&ch)==EOF)
{
printf("File is Empty");
}
fclose(f);
How about (not elegant way though )
int main( int argc, char* argv[] )
{
std::ifstream file;
file.open("example.txt");
bool isEmpty(true);
std::string line;
while( file >> line )
isEmpty = false;
std::cout << isEmpty << std::endl;
}
use this:
data.peek() != '\0'
I've been searching for an hour until finaly this helped!
pFile = fopen("file", "r");
fseek (pFile, 0, SEEK_END);
size=ftell (pFile);
if (size) {
fseek(pFile, 0, SEEK_SET);
do something...
}
fclose(pFile)
if (nfile.eof()) // Prompt data from the Priming read:
nfile >> CODE >> QTY >> PRICE;
else
{
/*used to check that the file is not empty*/
ofile << "empty file!!" << endl;
return 1;
}

Why does this code always return file size zero?

Why, when I use following code snippet the result is zero regardless of the file size, but when I remove ios::binary in open() it does what it's supposed to do?
fstream f1;
streampos begin, end;
f1.open("file1", ios::binary);
f1.seekg(0, ios::beg);
begin = f1.tellg();
f1.seekg(0, ios::end);
end = f1.tellg();
f1.close();
cout << end - begin << endl;
I assume that by "when I remove ios::binary" you mean you remove the entire argument:
f1.open("file1");
The function open() has two parameters - file name and mode. The mode one has a default argument of std::ios_base::in | std::ios_base::out. So if you don't specify anything, this deault gets used.
If you specify ios::binary, however, you replace the default argument. And since you have specified neither in nor out, the open() call fails. Putting an if() around the open() would tell you — remember you should always check for error with I/O.
std::ios_base::binary by itself is not a valid openmode for std::basic_fstream. The valid openmode combinations can be found on Table 132:
The constructor of std::basic_fstream and its open() method both forward the open() method on the internal std::basic_filebuf through rdbuf()->open(s, mode) where mode is the openmode. As you can see from the table, mode (where mode is ios_base::binary) by itself is not a valid flag. When the file buffer determines this the open fails:
The NTBS modstr is determined from mode & ~ios_base::ate as indicated in Table 132. If mode is not some combination of flags shown in the table then the open fails.
The reason mode isn't bitwise-OR'ed with out | in when opening the file is because it's not clear whether you want to use the stream for input or output (it assumes you know what you want). Since the mode isn't a valid flag combination, std::basic_filebuf::open() returns a null pointer. This is picked up by the stream which in turn calls setstate(std::ios_base::failbit).
[..] calls rdbuf()->open(s, mode). If that function returns a null pointer, calls setstate(failbit).
When a stream is in a fail state its tell methods return -1. That's why you're getting 0 when subtracting.
This is the correct way to open it if all you want is the size:
std::fstream f1("file1", std::ios_base::in | std::ios_base::ate | std::ios_base::binary);
std::cout << f1.tellg();
If you get a size of 0, check to see if the file opened...
#include<iostream>
#include<fstream>
using namespace std;
int main(){
fstream f1;
streampos begin, end;
f1.open("Data.txt", ios::binary);
if (f1.is_open()) {
cout << "Open" << endl;
}
f1.seekg(0, ios::beg);
begin = f1.tellg();
f1.seekg(0, ios::end);
end = f1.tellg();
f1.close();
cout << end - begin << endl;
system("pause");
}
If it didn't open, you may have tried opening a text file.
In text files the ios::binary flag is not included in opening mode. Text files are for storing text data and when binary input/output operations are tried on them, this can result in formatting transformations.

calculate size of file

i have following program to calculate size of file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(){
string line;
ifstream myfile ("C:\\Users\\7\\Desktop\\example\\text.txt",ios::in | ios::out |ios::binary);
if (!myfile){
cout<<"cannot open file";
exit (1);
}
while (!myfile.eof()){
getline(myfile,line);
cout<<line<<endl;
}
long l,m;
l=myfile.tellg();
myfile.seekg(0,ios::end);
m=myfile.tellg();
cout<<"size of text file is:";
cout<<(m-l)<<"bytes"<<endl;
myfile.close();
return 0;
}
for make more clarify in text.txt file i have wrote some copy of information from this site http://en.wikipedia.org/wiki/List_of_algorithms
but it shows me 0 bytes and why? what is wrong?
You are subtracting the current-file-position (l) from the end-of-file position (m) to get the size. This will work as you expect if the current-file-position is at the start of the file, but as you have just read the entire contents of the file, (l) is "starting" at the end of the file.
Just use the value of (m) rather than (m-l), as files always start at 0.
(Alternatively, before using ftell to get (l), use fseek to move to the start of the file)
#include <stdio.h>
int main(int argc, char** argv) {
FILE *f = fopen("x.txt", "r");
fseek(f, 0, SEEK_END);
printf("%ld\n", ftell(f));
fclose(f);
return 0;
}
while (!myfile.eof()){
getline(myfile,line);
cout<<line<<endl;
}
Reads the whole file, so the get pointer is already at the end of the file. myfile.seekg(0,ios::end) will not move it, so m-l will return 0.
Ok, another dopey question, why not use FileInfo('file name') and use the length value stored?
It appears your while loop reads the file completely. Then you capture the position in l. Then you seek to m, which is also the position of l. Then you print their difference.
Did I miss something here??
Once you read to the end of the file, its fail bit gets set, and until you reset that, nothing else you do with the file will really accomplish much. Your loop for copying the file is also wrong (like virtually all that start with while (!file.eof())).
I'd try something like this:
std::string line;
while (getline(myfile, line))
std::cout << line << "\n";
// allow further use of the stream object to work:
myfile.clear();
// since we already read to the end, the current position is the length:
length = myfile.tellg();
Just a warning - be aware that some OS's provide for sparse files. If you open a file, write a byte, seek to start-of-file + 1,000,000,000, write another byte, then close the file, the intermediate bytes may not actually be written to disk. So, which is the size of the file? The two blocks actually allocated on disk, or the 1,000,000,000 bytes that is the offset of the final byte? Either could be the correct answer, depending upon what you are using the result for,

Why doesn't this program read (or write?) correctly from a .bin file? (C++)

I created this program:
#include <iostream>
#include <fstream>
using namespace std;
int main () {
fstream file;
file.open("test.bin", ios::in | ios::out | ios::binary);
if(!file.is_open())
{
return -1;
}
int n = 5;
int x;
file.write(reinterpret_cast<char*>(&n), sizeof(n));
file.read(reinterpret_cast<char*>(&x), sizeof(x));
std::cout<<x;
file.close();
std::cin.ignore();
return 0;
}
that's supposed to write an integer "n" into a .bin file "test.bin", then read data from "test.bin" into an integer "x", then displays "x" to the screen.
When I run the program, it displays not 5, but -842150451. Why does this occur, and how can I fix it?
Isn't the file.write() moving the current file pointer when you write it, causing you to read data from the first location AFTER the written data?
Insert file.seekg(0); between the read and write commands.
You have to reposition the file stream to the start of the file after you do the write in order to read the data you just wrote.
You should also check that the write wrote everything you expected it to, and whether the read actually read anything at all. The semi-random number is due to the read failing.
I agree with Jherico. You need a:
file.seekg (0, ios::beg);