-2

In some ZIP file I have file head.txt. I want to copy text from this file to TMemo on my form. Here's my code:

procedure TformMain.LoadProject(InputFileName: string);
var
  MS: TMemoryStream;
  zip: TZipForge;
  txt: string;
begin
  MS := TMemoryStream.Create;
  try
    zip := TZipForge.Create(nil);
    try
      with zip do begin
        FileName := InputFileName;
        OpenArchive(fmOpenReadWrite);
        ExtractToStream('head.txt', MS);
        CloseArchive;
      end;
    finally
      zip.Free;
    end;
    MS.Seek(0, soFromBeginning);
    SetLength(txt, MS.Size);
    MS.Write(txt[1], MS.Size);
  finally
    MS.Free;
  end;
  if Length(txt) > 0 then Memo1.Lines.Text := txt;
end;

But it doesn't work. In head.txt in my ZIP file is:

123456
abcdef
xxxx

and the result in Memo is:

auto-suggest dropdow

Thanks for help!

Michael
  • 41,989
  • 11
  • 82
  • 128
core4096
  • 101
  • 12
  • 1
    Why are you writing contents of txt variable into your memory stream? Shouldn't you rather be reading the contents from memory stream into txt variable? – SilverWarior Jul 22 '15 at 21:29
  • @SilverWarior: Nice catch. – MartynA Jul 22 '15 at 21:38
  • 1
    Simply use [`ExtractToString`](http://www.componentace.com/help/zf_guide/extracttostring.htm) method. – TLama Jul 22 '15 at 21:48
  • 1
    As for unexpected output string. That is just the result of the fact that when you increase the size of the string the data that has been previously written at the specific memory location that is now assigned to the string hasn't been initialized to some default value. – SilverWarior Jul 22 '15 at 21:48
  • 1
    @TLama Since OP is interested in showing the contents of that file in Memo I think it would be far better to use Memo.Lines.LoadFromStream(MS) instead to load the text from Memory Stream. Othervise he would be temporarily doubling the memory usage (storing text in txt variable and in Memo at the same time). Wouldn't he be? – SilverWarior Jul 22 '15 at 21:53
  • @Silver, `TStrings.LoadFromStream` copies the whole stream content into a `TBytes` buffer and then allocates one string for `TEncoding.GetString` result. `TMemoStrings.SetTextStr` allocates one string for the line break processing (at least in Delphi XE3). What happens inside the `ExtractToString` method I can't say. Plus for `TMemo.Lines.LoadFromStream` is that it helps you with encoding detection (though I believe that `ExtractToString` method decodes the string properly). – TLama Jul 22 '15 at 22:16

2 Answers2

4

The problem is that instead of reading data from Memory Stream into your txt variable using Read method you are actually writing data from your txt variable into your Memory Stream.

So your code should look more like this

procedure TformMain.LoadProject(InputFileName: string);
var
  MS: TMemoryStream;
  zip: TZipForge;
  txt: string;
begin
  MS := TMemoryStream.Create;
  try
    zip := TZipForge.Create(nil);
    try
      with zip do begin
        FileName := InputFileName;
        OpenArchive(fmOpenReadWrite);
        ExtractToStream('head.txt', MS);
        CloseArchive;
      end;
    finally
      zip.Free;
    end;
    MS.Seek(0, soFromBeginning);
    SetLength(txt, MS.Size);
    MS.Read(txt, MS.Size);
  finally
    MS.Free;
  end;
  if Length(txt) > 0 then Memo1.Lines.Text := txt;
end;

I haven't tested it out.

But since you want to load the text from that file into Memo you could simplify this by removing the txt variable and all the fuss needed with it and load the text into memo directly from the memory stream like this:

Memo1.Lines.LoadFromStream(MS);

So your final code should look like this:

procedure TformMain.LoadProject(InputFileName: string);
var
  MS: TMemoryStream;
  zip: TZipForge;
begin
  MS := TMemoryStream.Create;
  try
    zip := TZipForge.Create(nil);
    try
      with zip do begin
        FileName := InputFileName;
        OpenArchive(fmOpenReadWrite);
        ExtractToStream('head.txt', MS);
        CloseArchive;
      end;
    finally
      zip.Free;
    end;
    MS.Seek(0, soFromBeginning);
    Memo1.Lines.LoadFromStream(MS);
  finally
    MS.Free;
  end;
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
SilverWarior
  • 7,372
  • 2
  • 16
  • 22
  • These ways don't work for me. The output in Memo is several Chinese characters. – core4096 Jul 23 '15 at 07:04
  • 5
    @core Naturally we assumed that you were using an ANSI Delphi since the code in your question implies that. This is the answer that you should have accepted. After you'd given TLama the chance to adapt to the revelation that you use Unicode Delphi. It's frustrating when we try to help people understand and learn, but all they seem to want to do is paste code in without having to take the time or trouble to learn. Ask yourself, what have you learnt from this question? – David Heffernan Jul 23 '15 at 07:35
2

Try replacing this code:

MS.Seek(0, soFromBeginning);
SetLength(txt, MS.Size);
MS.Write(txt[1], MS.Size);

with a call to SetString

SetString(txt, PAnsiChar(MS.Memory), MS.Size);

like in this question

Community
  • 1
  • 1
nepb
  • 189
  • 7