Several topics (see Using C++ filestreams (fstream), how can you determine the size of a file? and C++: Getting incorrect file size) on how to measure a file size compute the difference between the beginning and the end of the file like that :
std::streampos fileSize( const char* filePath ){
std::streampos fsize = 0;
std::ifstream file( filePath, std::ios::binary );
fsize = file.tellg();
file.seekg( 0, std::ios::end );
fsize = file.tellg() - fsize;
file.close();
return fsize;
}
But instead of opening the file at the beginning, we can open it at the end and just take a measure, like that :
std::streampos fileSize( const char* filePath ){
std::ifstream file( filePath, std::ios::ate | std::ios::binary );
std::streampos fsize = file.tellg();
file.close();
return fsize;
}
Will it work ? And if not why ?
It should work just fine. The C++ standard says about std::ios::ate
ate - open and seek to end immediately after opening
There's no reason it would fail when a manual open-then-seek would succeed. And tellg is the same in either case.
Related
i don't know whats the problem with filesystem resize_file standart c++ 17 in visual studio. when i test the STL with manual. it can resize as expect. when using in conditional. its like a bug. the result output is split 2 parts. i test it a file 2MB size. the result i should expected is input file should be resize to 0 byte in the end.
std::ifstream input("D:/input.exe", std::ios::binary);
if (input.is_open())
{
std::ofstream output("D:/output.exe", std::ios::binary | std::ios::ate | std::ios::app);
auto p = std::filesystem::path("D:/input.exe");
std::vector<char> buffer(1048576);
do
{
long long setPosition;
if (std::filesystem::file_size(p) > buffer.size())
{
setPosition = (std::filesystem::file_size(p) - buffer.size());
input.seekg(setPosition);
}
else
{
input.seekg(0);
}
input.read(buffer.data(), buffer.size());
std::streamsize dataSize = input.gcount();
if (dataSize)
{
output.seekp(0);
output.write(buffer.data(), dataSize);
long long resizeFile = (std::filesystem::file_size(p) - dataSize);
std::filesystem::resize_file(p, resizeFile);
}
else
{
output.close();
input.close();
break;
}
} while (true);
}
else
{
std::cout << "File is not exist";
}
What you are trying to do will not work. resize_file will truncate the file at the end of the file, so when you read X bytes from the start of the file and then truncate the file, you will chop off X bytes at the end of the file. You will probably end up with both input.exe and output.exe containing the beginning of the original input.exe. What you should do is to truncate the file after you've read it. The below will make output.exe a copy of the original input.exe and input.exe will be 0 bytes.
#include <iostream>
#include <fstream>
#include <filesystem>
int main() {
auto p = std::filesystem::path("D:/input.exe");
std::ifstream input(p, std::ios::binary);
if (input)
{
std::ofstream output("D:/output.exe", std::ios::binary);
std::vector<char> buffer(1048576);
while (true) {
input.read(buffer.data(), buffer.size());
std::streamsize dataSize = input.gcount();
if (dataSize==0) break;
output.write(buffer.data(), dataSize);
}
std::filesystem::resize_file(p, 0);
}
else
{
std::cout << "File does not exist";
}
}
Below is my function to copy file from source to destination
void CopyFile(const std::string& source, const std::string& destination)
{
std::ifstream ifs(source.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
if (!ifs.good())
return;
std::ofstream ofs(destination.c_str(), std::ios::out | std::ios::binary);
const int bufferSize = 4;
char buffer[bufferSize];
std::streamoff streamSize = ifs.tellg();
std::streamoff iteration = streamSize / bufferSize;
int rem = streamSize % bufferSize;
ifs.seekg(std::ios::beg);
while (iteration > 0)
{
ifs.read(buffer, bufferSize);
ofs.write(buffer, bufferSize);
ifs.seekg(bufferSize, std::ios::cur);
ofs.seekp(bufferSize, std::ios::cur);
--iteration;
}
ifs.read(buffer, rem);
ofs.write(buffer, rem);
ifs.close();
ofs.close();
}
other approach where i open file in non binary mode working for me but not this one. what am i missing here?
I am trying to write a char* to a binary file.
This is what I have now.
void Write(char* fileName, char* pData)
{
ofstream binFile (fileName, ios::out | ios::binary);
if (binFile.open())
{
binFile.write((char*)&pData, sizeof(pData));
binFile.close();
}
}
void Read(char* fileName, char* pData)
{
ifstream binFile(fileName, ios::in | ios::binary);
if(binFile.open())
{
binFile.read(char*)&pData, sizeof(pData));
binFile.close
}
}
int main()
{
char* testData = "ABCdEFG"; // not real data
char* getTestData;
char* file = "C:\\testData.dat";
Write(file, testData);
Read(file, getTestData);
}
Test data will be of unknown length. May not always be the same.
When i run the program once, and write and read. I can get back the test data.
But when i stop the program and run it again, this time without writing. Just reading, i cannot get back the test data.
I don't really understand whats happening here.
Can some one explain it to me?
binFile.write((char*)&pData, sizeof(pData));
is wrong. It just writes the value of the pointer. It does not write the data.
You need to use:
binFile.write(pData, strlen(pData));
However, that won't be adequate to read the data back. To be able to read the data back, you'll need to write the size of the string first.
size_t len = strlen(pData);
binFile.write((char*)&len, sizeof(len));
binFile.write(pData, len);
And when reading the data back, you will need to use:
size_t len = 0;
binFile.read(char*)&len, sizeof(len));
binFile.read(pData, len);
and then, null terminate the string.
pData[len] = '\0';
PS
Make sure getTestData is properly initialized before using it to read the data.
char getTestData[100];
will be adequate for your test case.
Update
You can make your program a bit better by using std::string instead of char*. The size of the saved data can be more easily managed when a std::string is used.
void Write(std::string const& fileName, std::string const& data)
{
std::ofstream binFile(fileName, std::ios::out | std::ios::binary);
if (binFile.is_open())
{
size_t len = data.size();
binFile.write((char*)&len, sizeof(len));
binFile.write((char*)&data[0], len);
// No need. The file will be closed when the function returns.
// binFile.close();
}
}
void Read(std::string const& fileName, std::string& data)
{
std::ifstream binFile(fileName, std::ios::in | std::ios::binary);
if(binFile.is_open())
{
size_t len = 0;
binFile.read((char*)&len, sizeof(len));
data.resize(len);
binFile.read((char*)&data[0], len);
}
}
int main()
{
std::string file = "testData.dat";
std::string testData = "ABCdEFG";
Write(file, testData);
std::string getTestData;
Read(file, getTestData);
std::cout << getTestData << std::endl;
}
how can i prevent my record from overwrite c++
cin.get (terminator);
FILE * dat = fopen("Accounts.dat", "wb");
myAccount.username = myUsername;
myAccount.password = myPassword;
int n = 0;
int filenumber=0;
filenumber= n;
fseek(dat,sizeof(myAccount), ios_base :: end);
fwrite(&myAccount, sizeof(myAccount),ios_base ::app, dat);
fclose (dat);
Some theory: fseek is a C routine found in stdio.h ( <cstdio> in C++). It works with C file descriptors as in the following
#include <stdio.h>
int main ()
{
FILE * pFile;
pFile = fopen ("myfile.txt","w");
if (pFile!=NULL)
{
fseek ( pFile , 0 , SEEK_SET );
fputs ("fopen example",pFile);
fclose (pFile);
}
return 0;
}
Notice the SEEK_SET flag used with fseek.
ios_base::end and similar are C++ member constants used in conjunction of the <fstream> routines as in the following
#include <fstream> // std::fstream
int main () {
std::fstream fs;
fs.open ("test.txt", std::fstream::in | std::fstream::out | std::fstream::app);
fs << " more lorem ipsum";
fs.close();
return 0;
}
You should NEVER mix those at any time, it's not typesafe and even if it works there's no guarantee that it would, plus it's not portable and a terrible code practice as well.
There isn't much one could deduce from your small snippet, but if you're encountering weird or erroneous behavior, you should first check these notions out.
References:
http://www.cplusplus.com/reference/cstdio/fseek/
http://www.cplusplus.com/reference/fstream/fstream/open/
Do not mix FILE* with std::fstream. Use one or the other.
std::fstream File("Accounts.dat", std::ios::out | std::ios::binary | std::ios::app);
if (File.is_open())
{
File.write(reinterpret_cast<char*>(&myAccount), sizeof(myAccount));
File.close();
}
or if using C:
FILE* file = fopen("meh.dat", "ab+");
if (file)
{
fwrite(&myAccount, sizeof(myAccount), 1, file);
fclose(file);
}
Which one of the following would be the most efficient and why? I'm leaning towards the unique_ptr because I think that there is no copy being done when returning the data read. It's just a transfer of ownership of a pointer.
For example, I think that the string version would create a temporary string, read data into that, and upon returning, it'd copy its data into the assigned result?
However, I'm not sure that I'm right. Any ideas what I mean and which is the best?
UniquePtr:
std::unique_ptr<const char[]> ReadFile(const char* FileName)
{
std::fstream file(FileName, std::ios::in);
if (file.is_open())
{
file.seekg(0, std::ios::end);
std::size_t size = file.tellg();
std::unique_ptr<char[]> result(new char[size]);
file.seekg(0, std::ios::beg);
file.read(result.get(), size);
file.close();
return std::move(result);
}
return nullptr;
}
Vector:
std::string ReadFile(const char* FileName)
{
std::fstream file(FileName, std::ios::in);
if (file.is_open())
{
file.seekg(0, std::ios::end);
std::vector<std::int8_t> buffer(file.tellg());
file.seekg(0, std::ios::beg);
file.read(result.data(), result.size());
file.close();
return std::string(result.data());
}
return std::string();
}
String:
std::string ReadFile(const char* FileName)
{
std::fstream file(FileName, std::ios::in);
if (file.is_open())
{
std::string result = std::string();
file.seekg(0, std::ios::end);
result.resize(file.tellg());
file.seekg(0, std::ios::beg);
file.read(&result[0], result.size());
file.close();
return result;
}
return std::string();
}
std::string ReadFile(const char* FileName)
{
std::fstream file(FileName, std::ios::in);
std::string result = std::string();
if (file.is_open())
{
file.seekg(0, std::ios::end);
result.resize(file.tellg());
file.seekg(0, std::ios::beg);
file.read(&result[0], result.size());
file.close();
}
return result;
}
I have no proof , but if function has only one return , copy elision might be implemented by compiler , if there are 2 returns inside function ,
copy elision might not work as expected ~~~
The
return std::string(result.data());
will only work if the data is null-terminated.
Apart from that complication it needlessly copies from a vector to a string.
The std::string code is most natural for reading text (text mode open of file).
You won't see much of a performance difference since the file i/o dwarfs the rest, but anyway with C++03 you will most likely get Return Value Optimization (depends on compiler), and with C++11 you will get move optimization of the result if you just use std::move.