calculate size of file - c++

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,

Related

Deleting character from file at the current position of the file object

Please look at this minimal working example:
#include <iostream>
#include <fostream>
using namespace std;
int main()
{
fstream file;
file.open("asd.txt", ios_base::out);
file << "this is a sentence!" << endl;
///is it possible at this point to delete the last character, the exclamation mark, from the file asd.txt using the object "file"?
file.close();
return 0;
}
I am writing a sentence to a file asd.txt using a file object file. Is it possible to delete the character ! from asd.txt using file?
You can use a std::ofstream with seekp and write to overwrite the ! with a space ( I tried just deleting the ! or replacing it by '\0' but i can't seem to get that to work )
#include
#include
int main()
{
std::ofstream file;
file.open("asd.txt");
///is it possible at this point to delete the last character, the exclamation mark, from the file asd.txt using the object "file"?
//Yes!
file.write("this is a sentence!", 19); // writes 19 chars to the file
long pos = file.tellp(); // gets the current position of the buffer ( in this case 19)
file.seekp(pos - 1); // subtracts one from the buffer position ( now 18 )
// writes a space that is one char at the current position of the file ( 18, which overwrites the '!' that is in pos 19)
file.write("", 1);
file.close();
return 0;
}
There are many alternatives though, you can always just close the file, reopen the file with std::ios_base::trunc which will clear all the content in the file, Then you can write the string again only up until before the !
file.write("this is a sentence!", 18);
You can also store the string in a std::string and call std::string::pop_back() to remove the last character and then write that into the file after clearing the file contents. Just store the stream into a std::ostringstream and get the string from the ostringstream and then pop_back() the !.
It really depends on your use case, if you want to give more detail I'd be free to help you out a little more.

Decoding / Encloding Text File using Stack Library - Can't Encode Large Files C++

I am working on a program that can encode and then decode text in C++. I am using the stack library. The way the program works is that it first asks you for a cypher key, which you put in manually. It then asks for the file name, which is a text file. If it is a normal txt file, it encodes the message to a new file and adds a .iia files extension. If the text file already has a .iia file extension, then it decodes the message, as long as the cypher key is the same as the one used to encode it.
My program does encode and decode, but how many characters it decodes is determined by temp.size() % cypher.length() that is in the while loop in the readFileEncode() function. I think this is what is keeping the entire file from being encoded and then decoded correctly. Another words, the ending file after it has been decoded from say "example.txt.iia" back to "example.txt" is missing a large portion of the text from the original "example.txt" file. I tried just cypher.length() but of course that does not encode or decode anything then. The entire process is determined by that argument for the decoding and encoding.
I cannot seem to find out the exact logic for this to encode and decode all the characters in any size file. Here is the following code for the function that does the decoding and encoding:
EDIT: Using WhozCraig's code that he edited for me:
void readFileEncode(string fileName, stack<char> &text, string cypher)
{
std::ifstream file(fileName, std::ios::in|std::ios::binary);
stack<char> temp;
char ch;
while (file.get(ch))
temp.push(ch ^ cypher[temp.size() % cypher.length()]);
while (!temp.empty())
{
text.push(temp.top());
temp.pop();
}
}
EDIT: A stack is required. I am going to implement my own stack class, but I am trying to get this to work first with the stack library. Also, if there is a better way of implementing this, please let me know. Otherwise, I believe that there is not much wrong with this except to get it to go through the loop to encode and decode the entire file. I am just unsure as to why it stops at, say 20 characters sometimes, or ten characters. I know it has to do with how long the cypher is too, so I believe it is in the % (mod). Just not sure how to rewrite.
EDIT: Ok, tried WhozCraig's solution and I don't get the desired output, so the error now must be in my main. Here is my code for the main:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <cstdlib>
#include <cctype>
#include <stack>
using namespace std;
void readFileEncode(string fileName, stack<char> &text, string cypher);
int main()
{
stack<char> text; // allows me to use stack from standard library
string cypher;
string inputFileName;
string outputFileName;
int position;
cout << "Enter a cypher code" << endl;
cin >> cypher;
cout << "Enter the name of the input file" << endl;
cin >> inputFileName;
position = inputFileName.find(".iia");//checks to see if the input file has the iia extension
if (position > 1){
outputFileName = inputFileName;
outputFileName.erase(position, position + 3);// if input file has the .iia extension it is erased
}
else
//outputFileName.erase(position, position + 3);// remove the .txt extension and
outputFileName = inputFileName + ".iia";// add the .iia extension to file if it does not have it
cout << "Here is the new name of the inputfile " << outputFileName << endl; // shows you that it did actually put the .iia on or erase it depending on the situation
system("pause");
readFileEncode(inputFileName, text, cypher); //calls function
std::ofstream file(outputFileName); // calling function
while (text.size()){// goes through text file
file << text.top();
text.pop(); //clears pop
}
system("pause");
}
Basically, I am reading .txt file to encrypt and then put a .iia file extension on the filename. Then I go back through, enter the file back with the .iia extension to decode it back. When I decode it back it is gibberish after about the first ten words.
#WhozCraig Does it matter what white space, newlines, or punctuation is in the file? Maybe with the full solution here you can direct me at what is wrong.
just for information: never read file char by char it will take you hours to finish 100Mb.
read at least 512 byte(in my case i read directly 1 or 2Mb ==> store in char * and then process).
If I understand what you're trying to do correctly, you want the entire file rotationally XOR'd with the chars in the cipher key. If that is the case, you can probably address your immediate error by simply doing this:
void readFileEncode(string fileName, stack<char> &text, string cypher)
{
std::ifstream file(fileName, std::ios::in|std::ios::binary);
stack<char> temp;
char ch;
while (file.get(ch))
temp.push(ch ^ cypher[temp.size() % cypher.length()]);
while (!temp.empty())
{
text.push(temp.top());
temp.pop();
}
}
The most notable changes are
Opening the file in binary-mode using std::ios::in|std::ios::binary for the open-mode. this will eliminate the need to invoke the noskipws manipulator (which is usually a function call) for every character extracted.
Using file.get(ch) to extract the next character. The member will pull the next char form the file buffer directly if one is available, otherwise load the next buffer and try again.
Alternative
A character by character approach is going to be expensive any way you slice it. That this is going through a stack<>, which will be backed by a vector or deque isn't going to do you any favors. That it is going through two of them just compounds the agony. You may as well load the whole file in one shot, compute all the XOR's directly, then push them on to you stack via a reverse iterator:
void readFileEncode
(
const std::string& fileName,
std::stack<char> &text,
const std::string& cypher
)
{
std::ifstream file(fileName, std::ios::in|std::ios::binary);
// retrieve file size
file.seekg(0, std::ios::end);
std::istream::pos_type pos = file.tellg();
file.seekg(0, std::ios::beg);
// early exit on zero-length file.
if (pos == 0)
return;
// make space for a full read
std::vector<char> temp;
temp.resize(static_cast<size_t>(pos));
file.read(temp.data(), pos);
size_t c_len = cypher.length();
for (size_t i=0; i<pos; ++i)
temp[i] ^= cypher[i % c_len];
for (auto it=temp.rbegin(); it!=temp.rend(); ++it)
text.push(*it);
}
You still get your stack on the caller-side, but I think you'll be considerably happier with the performance.

C++: Reading and Sorting Binary Files

I've been scratching my head and putting this homework off for a couple days but now that I hunker down to try and do it I'm coming up empty. There's 4 things I need to do.
1) Read a binary file and place that data into arrays
2) Sort the list according to the test scores from lowest to highest
3) Average the scores and output it
4) Create a new binary file with the sorted data
This is what the binary data file looks unsorted
A. Smith 89
T. Phillip 95
S. Long 76
I can probably sort since I think I know how to use parallel arrays and index sorting to figure it out, but the reading of the binary file and placing that data into an array is confusing as hell to me as my book doesn't really explain very well.
So far this is my preliminary code which doesn't really do much:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <Windows.h>
using namespace std;
int get_int(int default_value);
int average(int x, int y, int z);
int main()
{
char filename[MAX_PATH + 1];
int n = 0;
char name[3];
int grade[3];
int recsize = sizeof(name) + sizeof(int);
cout << "Enter directory and file name of the binary file you want to open: ";
cin.getline(filename, MAX_PATH);
// Open file for binary write.
fstream fbin(filename, ios::binary | ios::in);
if (!fbin) {
cout << "Could not open " << filename << endl;
system("PAUSE");
return -1;
}
}
Sorry for such a novice question.
edit: Sorry what the data file stated earlier is what it SHOULD look like, the binary file is a .dat that has this in it when opened with notepad:
A.Smith ÌÌÌÌÌÌÌÌÌÌÌY T. Phillip ÌÌÌÌÌÌÌÌ_ S. Long ip ÌÌÌÌÌÌÌÌL J. White p ÌÌÌÌÌÌÌÌd
Reading a file in c++ is simple:
create a stream from file [so that to read from the stream] (you have filestream[input/output], stringstream ... )
ifstream fin; //creates a fileinput stream
fin.open(fname.c_str(),ifstream::binary); // this opens the file in binary mod
void readFile(string fname)
{
ifstream fin;
fin.open(fname.c_str()); //opens that file;
if(!fin)
cout<<"err";
string line;
while(getline(fin,line)) //gets a line from stream and put it in line (string)
{
cout<<line<<endl;
//reading every line
//process for you need.
...
}
fin.close();
}
as you specify, the file is simply a text file, so you can process each line and do whatever you want.
Reading from a binary file may seem confusing, but it is really relatively simple. You have declared your fstream using your file name and set it to binary, which leaves little to do.
Create a pointer to a character array (typically called a buffer, since this data is typically extracted from this array after for other purposes). The size of the array is determined by the length of the file, which you can get by using:
fbin.seekg(0, fbin.end); //Tells fbin to seek to 0 entries from the end of the stream
int binaryLength = fbin.tellg(); //The position of the stream (i.e. its length) is stored in binaryLength
fbin.seekg(0, fbin.beg); //Returns fbin to the beginning of the stream
Then this is used to create a simple character array pointer:
char* buffer = new char[binaryLength];
The data is then read into the buffer:
fbin.read(buffer, binaryLength);
All the binary data that was in the file is now in the buffer. This data can be accessed very simply as in a normal array, and can be used for whatever you please.
The data you have, however, does not at all seem binary. It looks more like a regular text file. Perhaps, unless explicitly stated, you ought to consider a different method for reading your data.
You know, with that low range of sorting index you can avoid actual sorting (with comparing indices and moving data forth and back). All you have to do is to allocate a vector of vector of strings, resize it to 101. Then traverse the data, storing each: "A. Smith" in 89-th element; "T. Phillip" in 95-th; "S. Long" in 76-th and so on.
Then by iterating the vector elements from begin() to end() you would have all the data already sorted.
It's almost linear complexity (almost, because allocation/resizing of subvectors and strings can be costly) easy and transparent.

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

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.

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);