From: winapi on
Hi there,

I am trying to save a simple "structure" for an application I am making in
windows.
I have been using unicode. I have been trying to save the data uisng using
"fstream", not
sure if this is needed when uing the windows API.

Anyway, I read that you can use "wofstream" instead of just "ofstream" for
wchar_t.
The simple example below works with the "char" example, but not the
"wchar_t" example.
it just saves a blank file. I can't seem to find an example of binary data
being written using
wchar_t only text examples. Maybe "wofstream" does not read/write unicode as
expected without
further manipulation, any pointers would be most helpful. . . . . . . .


#include <windows.h>
#include <iostream>
#include <fstream>

using namespace std;

// wchar_t example.
// Not Working.

#define FILE_NAME L"outFile.bin"

struct SDATA
{
wchar_t *str;
int num;

SDATA()
{
str = NULL;
num = 0;
}
};

void SaveFile(wchar_t *file)
{
SDATA data;

data.str = L"String";
data.num = 10;

wofstream fout;

fout.open(file, ios::binary);

fout.write((wchar_t *)(&data), sizeof(data));

fout.close();
}



//=========================================================================

/*

// char example.
// Working.

#define FILE_NAME "outFile.bin"

struct SDATA
{
char *str;
int num;

SDATA()
{
str = NULL;
num = 0;
}
};

void SaveFile(char *file)
{
SDATA data;

data.str = "String";
data.num = 10;

ofstream fout;

fout.open(file, ios::binary);

fout.write((char *)(&data), sizeof(data));

fout.close();
}

*/


// Simple "console" test.

int main(void)
{
SaveFile(FILE_NAME);

return 0;
}



Thanks.





From: Jackie on
On 5/31/2010 21:23, winapi wrote:
> Hi there,
>
> I am trying to save a simple "structure" for an application I am making in
> windows.
> I have been using unicode. I have been trying to save the data uisng using
> "fstream", not
> sure if this is needed when uing the windows API.
>
> Anyway, I read that you can use "wofstream" instead of just "ofstream" for
> wchar_t.
> The simple example below works with the "char" example, but not the
> "wchar_t" example.
> it just saves a blank file. I can't seem to find an example of binary data
> being written using
> wchar_t only text examples. Maybe "wofstream" does not read/write unicode as
> expected without
> further manipulation, any pointers would be most helpful. . . . . . . .
>
> [snip]
>
> Thanks.
>

First of all, I see a few problems with your approach:
* You're trying to write a pointer to a string.
* I wouldn't use wofstream to write binary data, but ofstream instead.

Now, I'll answer you why I think wofstream::write fails for you...
Please note that:
* After fout.write(), fout.good() == false (fout.eof() == true).
* I am not sure but I think maybe in a 32-bit application,
"fout.write((wchar_t *)(&data), sizeof(data));" would try to write
(sizeof(data) characters). That's 8 wide characters in your example
which again is 16 bytes. sizeof(SDATA) is only 8 bytes.

I will guess that it fails because you may be writing characters that is
not within the unicode character range (somewhere in the pointer to str
or the integer).

Going back to writing a pointer to the string...
Take a look at this example memory dump of "data":
----------------------------------------
pointer to str
v
000ce930 0000000a 0�......
^
10
----------------------------------------

That's what you're writing to the file.

To write it properly with ofstream, you could do it like this:
----------------------------------------
fout.write(data.str, lstrlenA(data.str) + sizeof(char));
fout.write(reinterpret_cast<char*>(&data.num), sizeof(int));
----------------------------------------

You can also solve this by either using a character array in SDATA like
this:
----------------------------------------
struct SDATA
{
char str[100];
int num;

SDATA()
{
memset(str, 0, sizeof(str));
num = 0;
}
};
----------------------------------------

Your code in SaveFile then changes to something like the following:
----------------------------------------
void SaveFile(char *file)
{
SDATA data;

lstrcpyA(data.str, "String");
data.num = 10;

ofstream fout;

fout.open(file, ios::binary);

fout.write(reinterpret_cast<char*>(&data), sizeof(data));

fout.close();
}
----------------------------------------

However, this way, the file will have wasted space (sizeof(SDATA) == 104
bytes) so I would use the other method if strings are of varied lengths.
From: Jackie on
On 6/1/2010 00:32, Jackie wrote:
> * I am not sure but I think maybe in a 32-bit application,
> "fout.write((wchar_t *)(&data), sizeof(data));" would try to write
> (sizeof(data) characters). That's 8 wide characters in your example
> which again is 16 bytes. sizeof(SDATA) is only 8 bytes.

Rephrasing that:
* I think "fout.write((wchar_t *)(&data), sizeof(data));" would try
to write sizeof(data) *characters*. In a 32-bit application, the pointer
would be 4 bytes so (sizeof(SDATA) == 8). It would try to write 8 wide
characters, which is 16 bytes.

> fout.write(reinterpret_cast<char*>(&data.num), sizeof(int));

Non-important but sizeof(data.num) looks better.

> lstrcpyA(data.str, "String");

Please don't use this without checking the length of the string first
before trying to copy it into data.str, or a buffer overrun may occur.
It would be safer to use strcpy_s if you don't check the length yourself.
From: Ulrich Eckhardt on
winapi wrote:
> I am trying to save a simple "structure" for an application I am making in
> windows. I have been using unicode. I have been trying to save the data
> uisng using "fstream", not sure if this is needed when uing the windows
> API.

Needed, no. What I'm wondering is what file format you want. Actually, if
your answer is "Unicode" or "binary", I'm afraid that you don't know. This
is crucial for reading and writing correctly. My suggestion is to generally
go for textual representations like JSON or XML as metaformat and using
UTF-8 as encoding.

> Anyway, I read that you can use "wofstream" instead of just "ofstream"
> for wchar_t. The simple example below works with the "char" example,
> but not the "wchar_t" example.
> it just saves a blank file. I can't seem to find an example of binary data
> being written using
> wchar_t only text examples. Maybe "wofstream" does not read/write unicode
> as expected without further manipulation, any pointers would be most
> helpful. . . . . . . .

FYI: Unicode is _not_ a file format.

> #define FILE_NAME L"outFile.bin"

// Don't use macros.
wchar_t const* const filename = L"outFile.bin";

> struct SDATA
> {
> wchar_t *str;
> int num;
>
> SDATA()
> {
> str = NULL;
> num = 0;
> }
> };

Several points:
* Who owns the memory?
* Use initialisation lists.
* Why not use std::wstring?
* Copy constructor, assignment operator, destructor?
* Use ALL_UPPERCASE for macros only. Yes, the win32 API also uses it for
its types, but this is commonly agreed.

> void SaveFile(wchar_t *file)

wchar_t const* file

> {
> SDATA data;
>
> data.str = L"String";
> data.num = 10;
>
> wofstream fout;
>
> fout.open(file, ios::binary);

The "binary" only affects the handling of line endings. Further, for the
record, there is no open() function taking a wchar_t string in the
standard, that is an MS extension. Also, you could initialise the ofstream
with the filename directly istead of calling open() separately. Oh, and I
don't see you checking the results.

> fout.write((wchar_t *)(&data), sizeof(data));

1. Do not use C-style casts, you will only shoot yourself in the foot. In
order to write files, you can use a buffer of unsigned chars and then
format your data in there before writing it to the stream.
2. C++ IOStreams are for textual file formats generally. Plain char-streams
can be used for writing arbitrary binary data though, but you must avoid
the formatting functions, only unformatted ones like read() and write().
3. Writing an address that can change with every program call to disk is
useless. If you think you are writing the L"String" somewhere, be informed
that you don't.


Use this in order to prepare a filestream for bytewise writing:

std::ofstream out(filename, std::ios_base::binary);
out.imbue(std::locale::classic());

> fout.close();
> }

The stream is closed automatically here, no need for that. What you should
do is flush the stream and check its state afterwards, to at least detect
output errors.

Uli

--
Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932

From: winapi on
Thanks for all the helpful suggestions, I will have a good look into these
methods.