views:

565

answers:

3

I'm converting a D2006 program to D2010. I have a value stored in a single byte per character string in my database and I need to load it into a control that has a LoadFromStream, so my plan was to write the string to a stream and use that with LoadFromStream. But it did not work. In studying the problem, I see an issue that tells me that I don't really understand how conversion from AnsiString to Unicode string works. Here is a piece of standalone code that illustrates the issue I am confused by:;

procedure TForm1.Button1Click(Sender: TObject); {$O-}
var
  sBuffer: String;
  oStringStream: TStringStream;
  sAnsiString: AnsiString;
  sUnicodeString: String;
  iSize1,
  iSize2: Word;
begin
  sAnsiString := '12345';
  oStringStream := TStringStream.Create(sBuffer);
  sUnicodeString := sAnsiString;
  iSize1 := StringElementSize(sAnsiString);
  iSize2 := StringElementSize(sUnicodeString);
  oStringStream.WriteString(sUnicodeString);
end;

If you break on the last line, and inspect the Bytes property of oStringStream, you will see that it looks like this:

Bytes (49 {$31}, 50 {$32}, 51 {$33}, 52 {$34}, 53 {$35}

I was expecting that it might look something like

(49 {$31}, 00 {$00}, 50 {$32}, 00 {$00}, 51 {$33}, 00 {$00}, 
 52 {$34}, 00 {$00}, 53 {$35}, 00 {$00} ...

Apparently my expectations are in error. But then, how to convert an AnsiString to unicode?

I'm not getting the right results out of the LoadFromStream because it is reading from the stream two bytes at a time, but the data it is receiving is not arranged that way. What is it that I should do to give the LoadFromStream a well formed stream of data based on a unicode string?

Thank you for your help.

+4  A: 

What is the type of the oStringStream.WriteString's parameter? If it is AnsiString, you have an implicit conversion from Unicode to Ansi and that explains your example.


Updated: Now the real question is how TStringStream stores data internally. In the following code sample (Delphi 2009)

procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
  SS: TStringStream;

begin
  S:= 'asdfg';
  SS:= TStringStream.Create(S);  // 1 byte per char
  SS.WriteString('321');
  Label1.Caption:= SS.DataString;
  SS.Free;
end;

TStringStream uses internally the default system ANSI encoding (1 byte per char). The constructor and WriteString procedures convert a string argument from unicode to ANSI.

To override this behaviour you must declare the encoding explicitely in the constructor:

procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
  SS: TStringStream;

begin
  S:= 'asdfg';
  SS:= TStringStream.Create(S, TEncoding.Unicode);  // 2 bytes per char
  SS.WriteString('321');
  Label1.Caption:= SS.DataString;
  SS.Free;
end;
Serg
sBuffer is declared as:var sBuffer: UnicodeString;
A: 

I think you want to use:

LoadFromStream(stream, TEncoding.ASCII);

If your single byte text is not ASCII but is based on a code page, then this might work:

LoadFromStream(stream, TEncoding.GetEncoding(1252));

where the "1252" is the code page that your single byte text is based on.

lkessler
The LoadFromStream is a method of AdvStringrid from TMS. It only takes one parameter.
I don't use TMS, but maybe TMS's TTntStringGrid which is in their Unicode Component Pack may be able to do it for you. See: http://www.tmssoftware.com/site/tmsuni.asp Otherwise, I'd recommend you contact TMS and tell them your problem, and they may add the 2nd parameter to their LoadFromStream to make it Delphi 2009+ Unicode compatible.
lkessler
This does not appear to be a problem with the grid. Please see my edits to my original post. It does not appear that casting an AnsiString to unicode changes the internal formatting of the string.
It's the whole TEncoding system that they added into Delphi that is designed to handle that exact problem, so that the casting will be correct. So maybe a solution for you is to load your stream into another stream using the encoding that works, and then load that into your AdvStringrid.
lkessler
A: 

The stream format largely depends on the TStringStream.Encoding. In your exemple, the used codepage should be the same as sBuffer (See implentation from TStringStream.Create).

Since oStringStream.WriteString(sUnicodeStream); seems to save as single bytes, I'd assume sBuffer is an Ansistring or a RawByteString.

Now... why do the reading fails... You have yet to supply us an example of how you do read back in that stream.

Ken Bourassa
So, like me, you expect that if sUnicodeStream is declared as a UnicodeString, you would see a string composed of two bytes per character. If you try out my newly edited example code you will see that it apparently does not work that way.
Serg is right... TStringStream only check the codepage in the ansistring version.
Ken Bourassa