[SOLVED] Using TFileStream to write plan old text files lazarus / 62-General ---------------------------------------------------------------------- dbannon · 2017-08-05 02:15 TFileStream does not behave as I expect when I send it a series of strings, have tried with both shortstring and ANSIstring.  fsOut.Write(S1, length(S1)) makes what appears as a binary or data file. Using S1[1] does what I need. Here is an example - Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program SaveStringToPathDemo;{$mode objfpc}uses  Classes, sysutils;var  fsOut : TFileStream;  S1, S2, S3 : ANSIstring; begin    S1 := 'This is some text '#10;    S2 := 'And this is some more ';    S3 := 'This is probably enough.'#10;    fsOut := TFileStream.Create('TestFile1.txt', fmCreate);    fsOut.Write(S1, length(S1));    fsOut.Write(S2, length(S2));    fsOut.Write(S3, length(S3));    fsOut.Free;    fsOut := TFileStream.Create('TestFile2.txt', fmCreate);    fsOut.Write(S1[1], length(S1));    fsOut.Write(S2[1], length(S2));    fsOut.Write(S3[1], length(S3));    fsOut.Free;end. Running this writes two file, only the second is a clean text file. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i db:~/ObjectPascal$ file Test*TestFile1.txt: dataTestFile2.txt: ASCII text Now, finally, my question. Is this a bug or is that an "undocumented Feature" ?  If the latter, I'll document it on eg http://wiki.freepascal.org/File_Handling_In_Pascal My searches indicate that this is the behaviour since, at least 2009 so "fixing" it would be pretty brave I'd expect. But newcomers like me need to be told (I say newcomer in that last time I used Pascal/Delphi, the streams approach did not exist). David ---------------------------------------------------------------------- taazz · 2017-08-05 02:44 Quote from: dbannon on August 05, 2017, 04:15:02 amNow, finally, my question. Is this a bug or is that an "undocumented Feature" ?  If the latter, I'll document it on eg http://wiki.freepascal.org/File_Handling_In_Pascal Neither, its the result of untyped constants and misunderstanding what the modern string variable holds. You can think of a string as a pointer to an array of characters. The variable Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  vStr:String;begin  vStr := 'This is a string';end;  holds the memory address of the first character of the string in the heap. passing a string in an untyped paramater you pass the address of the pointer. It has been like this from the delphi 2 days that huge strings introduced circa 1995. ---------------------------------------------------------------------- RAW · 2017-08-05 02:49 This works too... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Procedure TForm1.FormClick(Sender: TObject);  Var   fs : TFileStream;   str: String; Begin  str:= 'Hello Hello Hello';   fs:= TFileStream.Create('Hello.txt', fmCreate);   Try    fs.WriteBuffer(Pointer(str)^, Length(str));   Finally    fs.Free;   End; End; ---------------------------------------------------------------------- dbannon · 2017-08-05 02:56 Hi taazz, thanks for that. Yes, aware of how it happens now but my point is its not what an uninformed user would expect. The function accepts a string, other functions that accept a string know not to use the first char (string) or the management block (ANSIstring). I sure agree that changing the behaviour is totally off the agenda but am asking why I should not document what I found. I spent, maybe, 2 hours looking at this, I'd like to prevent other people from making the same assumptions. But new enough to want to show my plan to the more experienced before editing the wiki .... So, streams been there since Delphi 2 ?  I used only Delphi 1, long time ago ! Thanks mate ! David ---------------------------------------------------------------------- howardpc · 2017-08-05 03:01 You could use the filestream methods dedicated to strings, and not worry about the file format itself. e.g. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program SaveStringToPathDemo; {$mode objfpc}{$H+} uses  Classes, sysutils; const  File_Name = 'TestFile1.txt'; var  fs: TFileStream;  S1, S2, S3 : AnsiString; begin    S1 := 'This is some text '#10;    S2 := 'And this is some more ';    S3 := 'This is probably enough.'#10;  fs:=TFileStream.Create(File_Name, fmCreate);    try      fs.WriteAnsiString(S1);      fs.WriteAnsiString(S2);      fs.WriteAnsiString(S3);    finally      fs.Free;    end;     s1:=''; s2:=''; s3:='';    fs:=TFileStream.Create(File_Name, fmOpenRead);    try      s1:=fs.ReadAnsiString;      s2:=fs.ReadAnsiString;      s3:=fs.ReadAnsiString;    finally      fs.Free;    end;    WriteLn(s1);    WriteLn(s2);    WriteLn(s3);    ReadLn;end. ---------------------------------------------------------------------- RAW · 2017-08-05 03:02 Quote...uninformed user would expect. Maybe uninformed users would take a look at the wiki... It takes seconds to find several examples with str[1]...  No harm meant ... ---------------------------------------------------------------------- taazz · 2017-08-05 03:05 Quote from: dbannon on August 05, 2017, 04:56:28 amHi taazz, thanks for that. Yes, aware of how it happens now but my point is its not what an uninformed user would expect. The function accepts a string, other functions that accept a string know not to use the first char (string) or the management block (ANSIstring). No it does not accept a string, it accepts a buffer any buffer. If it was of type string you would not be able to pass anything but strings. Quote from: dbannon on August 05, 2017, 04:56:28 amI sure agree that changing the behaviour is totally off the agenda but am asking why I should not document what I found. I spent, maybe, 2 hours looking at this, I'd like to prevent other people from making the same assumptions. But new enough to want to show my plan to the more experienced before editing the wiki .... So, streams been there since Delphi 2 ?  I used only Delphi 1, long time ago ! Thanks mate ! David It is valuable to have the behavior documented as a caution for the new comers I agree, the exact wording or presentation of the behavior I leave it to more experienced members. ---------------------------------------------------------------------- taazz · 2017-08-05 03:07 Quote from: howardpc on August 05, 2017, 05:01:37 amYou could use the filestream methods dedicated to strings, and not worry about the file format itself. Have you looked at those method your self? they inject the length of the string infront of every string written. that might be acceptable and might not. ---------------------------------------------------------------------- dbannon · 2017-08-05 03:45 Quote from: RAW on August 05, 2017, 05:02:02 amMaybe uninformed users would take a look at the wiki... It takes seconds to find several examples with str[1]...  Well, no, not really. It took me (and I can be as uninformed as anyone) over two hours to find  http://free-pascal- general.1045716.n5.nabble.com/Creating-text-files-with-TFileStream- td2824859.html   And thats obviously not on THE wiki. There is one example on http://wiki.freepascal.org/File_Handling_In_Pascal, is slipped into the code without any explanation or attempt to draw a reader's attention. Yep, I missed it after going over that page several times, sigh ... Anyway, I think RAW's approach, Pointer(str)^ is better in that its more obvious in what its doing. Wordier but clearer I think. I also think the wiki needs a few words added and I'll do unless someone says 'no'. ---------------------------------------------------------------------- RAW · 2017-08-05 03:50 EDIT: The second hit has got 2 examples... that's not bad... ---------------------------------------------------------------------- dbannon · 2017-08-05 03:56 Quote from: howardpc on August 05, 2017, 05:01:37 amYou could use the filestream methods dedicated to strings, and not worry about the file format itself. e.g.        fs.WriteAnsiString(S1);   Actually howardpc, I tried that before posting. It does not produce a clean ascii text file. Did you try it yourself  ? Be interesting to know if you cat'ed the file at the console or relied on your programme being able to re-read it ? I suspect some (eg) editors, trying to be helpful, filter out some things they think, quite rightly, don't belong. ---------------------------------------------------------------------- Handoko · 2017-08-05 04:38 TFileStream.WriteAnsiString works as it should be. It doesn't mean to produce text file. It actually write the string's length first before the string's content, and it does not write #13 after the end of the string. ---------------------------------------------------------------------- RAW · 2017-08-05 05:05 This works for me... (Read file...) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Procedure TForm1.FormClick(Sender: TObject);  Var   fs : TFileStream;   str: String; Begin  str:= Application.Location+'LAZARUS.txt';   If FileExists(str)  Then   Begin    fs:= TFileStream.Create(str, fmOpenRead);     Try      SetLength(str, fs.Size);      fs.ReadBuffer(Pointer(str)^, Length(str)); // or fs.Read(str[1], fs.Size);     Finally      fs.Free;     End;    ShowMessage(str);   End; End; ---------------------------------------------------------------------- howardpc · 2017-08-05 07:25 Quote from: dbannon on August 05, 2017, 05:56:31 amQuote from: howardpc on August 05, 2017, 05:01:37 amYou could use the filestream methods dedicated to strings, and not worry about the file format itself. e.g.        fs.WriteAnsiString(S1);   Actually howardpc, I tried that before posting. It does not produce a clean ascii text file. Did you try it yourself  ? Be interesting to know if you cat'ed the file at the console or relied on your programme being able to re-read it ? I suspect some (eg) editors, trying to be helpful, filter out some things they think, quite rightly, don't belong. My comment to not worry about the file format itself was intended to indicate that TFileStream ReadAnsiString/WriteAnsiString are not designed to create traditional .txt files (structured only by CR/LF) but binary files that can be read and written consistently using streaming, just as Readln/Writeln can be used for reading and writing traditional .txt files using classic Pascal routines. Their advantage is the increased speed of reading this gives since extra string length information is stored in the file (so does not have to be calculated by parsing as the file is read as with a .txt file). The disadvantage is the exotic format, and the slightly larger file size. ---------------------------------------------------------------------- dbannon · 2017-08-05 08:50 Quote from: howardpc on August 05, 2017, 09:25:48 amMy comment to not worry about the file format itself was intended to indicate that TFileStream ReadAnsiString/WriteAnsiString are not designed to create traditional .txt files (structured only by CR/LF) but binary files that can be read and written consistently using streaming, just as Readln/Writeln can be used for reading and writing traditional .txt files using classic Pascal routines. Their advantage is the increased speed of reading this gives since extra string length information is stored in the file (so does not have to be calculated by parsing as the file is read as with a .txt file). The disadvantage is the exotic format, and the slightly larger file size. Ah, I see what you mean. Might suit some others but certainly not me. I'm writing out some xml to be read in another application so must be plain old text file I'm afraid. Anyway, thanks everyone for you input, great to get such a response. Think we are done here. ---------------------------------------------------------------------- Thaddy · 2017-08-05 08:59 Quote from: dbannon on August 05, 2017, 10:50:07 amAh, I see what you mean. Might suit some others but certainly not me. I'm writing out some xml to be read in another application so must be plain old text file I'm afraid. If you are writing xml, just use a TFilestream and  write buffered, do not use the string functions in that case. Point is: XML doesn't care about line endings or zero termination at all, so provided your other application expects valid XML that should simply work. You will loose some text formatting (not within the XML tags, though), but that formatting is not part of XML itself..... It is just to make it easy on the eyes.