Recent

Author Topic: [SOLVED] Struggling with use of TFileStreamWriteAnsiString  (Read 5890 times)

Gizmo

  • Hero Member
  • *****
  • Posts: 831
[SOLVED] Struggling with use of TFileStreamWriteAnsiString
« on: September 10, 2016, 01:35:00 am »
I'm having some issues with TFileStream.WriteAnsiString in that I am using it in two programs for writing text to output files. In both cases, I am getting additional data around my strings (Unicode in fact).

My usgae is as simple as

var
  MyString : string;
begin
MyString := 'Hello';
MyFS.WriteAnsiString(MyString);
end;

Should I be doing something extra when using it, like specfiying a length or calling SetLength or something? I ask because the documentation is confusing me (http://www.freepascal.org/docs-html/rtl/classes/tstream.writeansistring.html) and I think perhaps related to the problem where it says : "The ansistring is written as a 4 byte length specifier, followed by the ansistring's content. "

For example, my code segment https://github.com/tedsmith/quickhash/blob/master/unit2.pas#L1159  here works in that the HTML output is written OK, but in the finished HTML file, there are loads of seemingly random values added just after  fsHTMLOutput.WriteAnsiString(strTableHeader); The more content my tables hold, the more of these values exist. So with a large list of thousands of files, my HMTL file has hundreds of lines of Unicode values embedded above the table. So it seems to be adding values for every iteration of the for loop. To see what I mean in a practical sense, try downloading the program (https://sourceforge.net/projects/quickhash/files/v2.6.9/), click on the "Compare Directories" tab, choose two folders with more or less the same files in them and then click "Save to File" when finished and open the HTML file.
« Last Edit: September 10, 2016, 03:17:04 pm by Gizmo »

lainz

  • Hero Member
  • *****
  • Posts: 4738
  • Web, Desktop & Android developer
    • https://lainz.github.io/
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #1 on: September 10, 2016, 03:02:43 am »

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #2 on: September 10, 2016, 03:55:07 am »
I don't understand what is difficult about this.

Are you expecting text only output that you have written using the filestream ?

Because that would be a wrong assumption in case you are using writeansistring method.

WriteAnsString saves 4 bytes (as integer value) to the stream telling how many bytes the string contains that follows. It wil do that for every WriteAnsiString you call, so the more WriteAnsiStrings you call the more length specifiers would be located inside your output file.

If you want to have text only output you should use something similar to:
Code: [Select]
Var
  FS: TFileStream;
  S : String;
begin
  ...
  S := 'Hello';
  FS.Write(S[1], Length(S));
  ...
end;
« Last Edit: September 10, 2016, 04:01:50 am by molly »

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #3 on: September 10, 2016, 04:19:54 am »
I would suggest that you
1-expand TFileStream (or the class you are using):
Code: Pascal  [Select][+][-]
  1. type
  2. ...
  3.   TTedFileStream=class(TFileStream)
  4.   public
  5.     procedure Add(const S:String);
  6.   end;
  7. ...
  8. procedure TTedFileStream.Add(const S:String);
  9. begin
  10.   Write(S[1], Length(S));
  11. end;
  12.  

2-Replace WriteAnsiString with Add.

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #4 on: September 10, 2016, 04:30:50 am »
I take it we do not have a textwriter class for fpc/lazarus ?

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #5 on: September 10, 2016, 04:35:55 am »
TStringList?

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #6 on: September 10, 2016, 04:46:30 am »
A stringlist could also be a solution for TS but, i find it difficult to compare against a filestream (or textwriter) class as a stringlist keeps everything in memory.

In that regards, your solution is/seems the quickest atm engkin

Especially since the method can be named to something TS would fancy. Personally i would probably have chosen for a helper class (edit: that should read) class helper but it is of course similar.

Oh, and i just noticed when browsing through your code @Gizmo. You might also want to consider to either A) add an additional method to engkin's solution that also writes a lineending to the given string or B) add that to the method as shown by engkin.

Even though html is allowed without newlines, it is difficult to read/edit in case a user wants to.
« Last Edit: September 10, 2016, 05:10:12 am by molly »

balazsszekely

  • Guest
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #7 on: September 10, 2016, 08:01:22 am »
Quote
@molly
 take it we do not have a textwriter class for fpc/lazarus ?
I believe TStringStream is what you are looking for.
« Last Edit: September 10, 2016, 08:19:55 am by GetMem »

Thaddy

  • Hero Member
  • *****
  • Posts: 18524
  • Here stood a man who saw the Elbe and jumped it.
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #8 on: September 10, 2016, 09:04:00 am »
I believe TStringStream is what you are looking for.

Or maybe a helper for TFilestream;
Code: Pascal  [Select][+][-]
  1. type
  2.   TFilestreamHelper = class helper for TFilestream
  3.     procedure WriteStr(const s:string);
  4.   end;
  5.   procedure TFilestreamHelper.WriteStr(const s:string);
  6.   begin
  7.     self.write(S[1],Length(s));
  8.   end;

But the extension example from engkin is my favorite. I first overlooked that, so I editted this to remove a similar example and changed mine to a type helper.

« Last Edit: September 10, 2016, 09:09:43 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #9 on: September 10, 2016, 09:06:01 am »
@getmem:
Unfortunately TStringStream stores it data into a (memory) string.

I agree that it has the readstring and writestring methods that could be of use but, it is far from ideal (unless i missed something). I am not even sure. Is there a way to 'link' a stringstream directly to a FileStream ?

Edit: oops, it seems that Thaddy was a bit faster with typing so our posts crossed. Sorry for the repeat.
« Last Edit: September 10, 2016, 09:13:52 am by molly »

Thaddy

  • Hero Member
  • *****
  • Posts: 18524
  • Here stood a man who saw the Elbe and jumped it.
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #10 on: September 10, 2016, 09:24:20 am »
No, not really. Extending TFilestream seems the better option.
Note that readstring and writestring are used for serialization and de-serialization of strings, hence they write and read the string's length as well.
The examples form engkin and me are useful for OP's purpose, but are not usable for (de-)serialization of course. The string's length not stored.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #11 on: September 10, 2016, 09:48:28 am »
@Thaddy:
Hmz, strange.

The readstring method requires a length to be specified and a quick test with writeString shows to me that it does not store the length specifiers so, i have to conclude no serialization is taking place when using TStringStream ? (never used the class myself, so had to test :) )

Thaddy

  • Hero Member
  • *****
  • Posts: 18524
  • Here stood a man who saw the Elbe and jumped it.
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #12 on: September 10, 2016, 10:58:48 am »
Right: TStringstream does not serialize. TFilestream does, however.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Gizmo

  • Hero Member
  • *****
  • Posts: 831
Re: Struggling with use of TFileStreamWriteAnsiString
« Reply #13 on: September 10, 2016, 03:16:52 pm »
Thanks guys. The solutions worked...

 FS.Write(s[1], Length(s));

When I first saw fs.write, I noticed it requested a buffer as parameter one, and I often forget that strings are in essence buffers but I typically think of buffers as big buckets of memory to store stuff in. I can't get used to strings being, in essence, the same thing. So that's why I plumbed for WriteAnsiString which I assumed would just 'write an ANSI string' and not "write and ansi string and the size of the string as well".

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12567
  • FPC developer.
Re: [SOLVED] Struggling with use of TFileStreamWriteAnsiString
« Reply #14 on: September 10, 2016, 04:31:04 pm »
Unit streamex contains some reader/writer classes.

But unit streamio allows to assign streams to file handles, and file handles means you can use compact and nice "writeln" syntax.

This is also a good workaround if you hit the path length limit of "assign" and don't want to change your code too much.

 

TinyPortal © 2005-2018