Re: Saving a binary file into a string
On Dec 27, 3:39 pm, Dominik Schmidt <dominikschmi...@gmx.net> wrote:
I'm new to C++, so I have a very basic question.
I wrote a function which opens a file and saves it into a
string variable. Another function can save a string variable
into a file. I tried to combine those two functions, because
in combination they should make an exact copy of a file. And
it seems to work, in every case the copy had the exact same
hash value (MD5) as the original file.
I'd like to know if I made an error, so if there's any
possibility that my functions won't work in some case?
The question is: how portable do you want to be?
Just to be sure: 1 Byte can have 2^8 = 256 different states,
so each "character" of a string variable can have those 256
states, right?
On most machines. (I know of at least one where bytes have 9
bits, and 512 possible states. And more than one where a signed
char would have 2^n-1 possible states, rather than 2^n; but on
all of those machines, plain char is unsigned---probably in
order to avoid such problems.)
Every byte of a file can be saved in a string variable?
Yes. Getting it from the file into the string variable isn't
necessarily trivial, however. In fact, it's impossible to do
"portably", if by portably, you mean for all legal
implementations of C++.
For this sort of thing, I'd generally prefer vector< char >, but
string will work too.
If this is true, there can be no file which can *not* be
copied by my functions (except it's too big)?
================
Here is my code:
================
int main()
{
string var1;
var1 = OpenFile("D:\\myfile1.bin");
SaveFile("D:\\myfile1-copy.bin", var1);
return 0;
Not related to your question, but if either OpenFile or SaveFile
fails, you shouldn't return 0.
}
string OpenFile(string FilePath)
{
string Out1;
ifstream file1;
char Chr1;
string Str1;
long long filesize;
Strictly speaking, long long isn't portable (yet). Also
strictly speaking, it may not be large enough to hold the size
of a file (but practically, I can't imagine this being a problem
anytime soon).
filesize = GetFileSize(FilePath);
file1.open (FilePath.c_str(), ios::binary);
if (file1.is_open())
{
for (long long i = 0; i < filesize; i++)
{
file1.read (&Chr1, 1);
Str1 = Chr1;
Attach (&Out1, Str1);
}
}
file1.close ();
All of which is doing things the hard way. Why not simply:
char tmp;
while ( file1.get( tmp ) )
Out1 += tmp;
if ( !file1.eof() )
// read error...
(once you've verified that the open has succeeded, of course)?
Or even:
string Out1( (std::istreambuf_iterator< char >( file1 )),
(std::istreambuf_iterator< char >()) );
(About the only way you'll get a read error here is through an
exception.)
return Out1;
}
void SaveFile(string FilePath, string FileContent)
{
ofstream file1;
long long filesize;
char Chr1;
filesize = FileContent.length();
if (FileExists(FilePath)) KillFile(FilePath);
if (FileExists(FilePath)) return;
if (filesize < 0) return;
If filesize < 0, here, you've got a very serious bug in your
implementation.
file1.open (FilePath.c_str(), ios::binary);
if (file1.is_open())
{
for (long long i = 0; i < filesize; i++)
{
Chr1 = FileContent[i];
file1.write (&Chr1, 1);
}
}
Again, what's wrong with:
for ( std::string::const_iterator iter = FileContent.begin();
iter != FileContent.end();
++ iter )
file1.put( *iter );
? Or just:
std::copy( FileContent.begin(), FileContent.end(),
std::ostreambuf_iterator< char >( file1 ) );
file1.close();
And you have to check the status of file1 after the close, and
report an error.
(While I'm at it, what's with these file1? It's output, or what
ever, or even file, given the context, but without the 1.)
}
//Here are the dependencies:
long long GetFileSize(string FilePath)
{
long long Out1 = -1;
ifstream file1;
file1.open (FilePath.c_str());
if (file1.is_open())
{
file1.seekg(0, ios::end);
Out1 = file1.tellg();
Formally, this is not guaranteed to work. Practically, it
doesn't work under Windows if the file is opened in text mode
(as you've done). Nor under most other systems: Unix is the
exception.
}
file1.close();
return Out1;
}
bool FileExists(string FilePath)
{
//http://msdn.microsoft.com/en-us/library/aa365740%28VS.85%29.aspx
WIN32_FIND_DATA FindFileData;
HANDLE hFind = FindFirstFile(FilePath.c_str(), &FindFileData);
if (hFind == INVALID_HANDLE_VALUE || (FindFileData.dwFileAttributes &
FILE_ATTRIBUTE_DIRECTORY))
{
return false;
}
else
{
return true;
}
}
If you're using system specific code... Use system specific
versions of open/read/write/close. They should offer the
necessary options for causing open to fail if the file doesn't
exist (input) or causing your file to replace any existing file
(output). For that matter, IIRC, this is guaranteed when you
open an ifstream or an ofstream, so you don't need any of this
anywhere.
void Attach(string *OldString, string AddString, bool InNewLine)
{
if (InNewLine)
{
*OldString += Cr;
*OldString += Lf;
}
*OldString += AddString;
And what on earth is this? (You only pass two arguments to
Attach, so your code shouldn't even compile.)
}
bool KillFile(string FilePath)
{
int Out1;
Out1 = remove(FilePath.c_str());
return Out1 == 0;
}
Just for the record, this can have interesting consequences
under Unix, and might not be what you want.
--
James Kanze