How do I prevent TStrings.SaveToFile creating a final empty line?64 base encoded string to binary fileWhy variables are declared as TStrings and created as TStringList?Convert one delphi code line to c++Modifying or deleting a line from a text file the low-level way?Delphi: Save Multiline Strings to FileHow do you extract local variable information (address and type) from a Delphi program or the compiler-generated debug info?Use RTTI to read and write enumerated property as IntegerHow do I read a file, wihout loading it into the memory, like the Delphi TFileStream class in D?Why EncdDecd.EncodeStream is returning a new line?

How do electric hot water heaters explode and what can be done to prevent that from happening?

Why/when does SQL Server evaluate the probe side of an inner hash join when the build side was empty?

In Germany, why does the burden of proof fall on authorities rather than the company or individual when it comes to possible illegal funds?

Do Adventure cards count towards "number of instant and sorcery cards in your graveyard"?

Is there any reason a person would voluntarily choose to have PMI?

Are soldered electrical connections code-compliant?

When did Hebrew start replacing Yiddish?

How to tell my Mom that I don't care about someone without upsetting her?

Can a German employer force mandatory overtime and forbid salary discussion?

Who verifies the trust of certificate authorities?

Intuitive explanation why some autonomous DEs go to infinity in finite time.

Is Jupiter still an anomaly?

Is there a material or method to allow "swimmable" coins?

What is the purpose of the rules in counterpoint composition?

Centered text and Equations aligned

Why do cargo airlines frequently choose passenger aircraft rather than aircraft designed specifically for cargo?

How does the ground effect affects longitudinal stability?

A single word for "not allowed to be changed" or "must be this way"

Should I still follow "programming to an interface not implementation" even if I think using concrete class members is the simpler solution?

Between while and do in shell script

In Alita: Battle Angel do cyborgs have stomachs?

Evil plans - how do you come up with interesting ones?

If I buy a super off-peak ticket, can I travel on a off-peak train and pay the difference?

What is latinum and where does it occur?



How do I prevent TStrings.SaveToFile creating a final empty line?


64 base encoded string to binary fileWhy variables are declared as TStrings and created as TStringList?Convert one delphi code line to c++Modifying or deleting a line from a text file the low-level way?Delphi: Save Multiline Strings to FileHow do you extract local variable information (address and type) from a Delphi program or the compiler-generated debug info?Use RTTI to read and write enumerated property as IntegerHow do I read a file, wihout loading it into the memory, like the Delphi TFileStream class in D?Why EncdDecd.EncodeStream is returning a new line?






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;









8


















I have a file .input.txt like this:



aaa
bbb
ccc


If I read it using TStrings.LoadFromFile and write it back (even without applying any changes) using TStrings.SaveToFile, it creates an empty line at the end of the output file.



var
Lines : TStrings;
begin
Lines := TStringList.Create;
try
Lines.LoadFromFile('.input.txt');

//...

Lines.SaveToFile('.output.txt');
finally
Lines.Free;
end;
end;


The same behavior can be observed using the TStrings.Text property which will return a string containing an empty line at its end.










share|improve this question



























  • just wondering, why on earth would you want to write it back even when there is no change applied in the file? why not just simply read it?

    – Bilal Ahmed
    Oct 17 at 7:27






  • 3





    @BilalAhmed: sure, it is a simplified test, the same empty line appear when applying changes to the string list

    – Fabrizio
    Oct 17 at 7:29











  • By "creates an empty line" I guess you mean that your original file does not end with the n character and the function adds the n to the file? Or does the function literally add a n right after an existing n at the end of file? POSIX requires text files to have all their lines terminated by a n, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminating n when you save files by default (e.g. vim, IDEs etc all by default make your files POSIX-compliant.)

    – Giacomo Alzetta
    Oct 17 at 15:49

















8


















I have a file .input.txt like this:



aaa
bbb
ccc


If I read it using TStrings.LoadFromFile and write it back (even without applying any changes) using TStrings.SaveToFile, it creates an empty line at the end of the output file.



var
Lines : TStrings;
begin
Lines := TStringList.Create;
try
Lines.LoadFromFile('.input.txt');

//...

Lines.SaveToFile('.output.txt');
finally
Lines.Free;
end;
end;


The same behavior can be observed using the TStrings.Text property which will return a string containing an empty line at its end.










share|improve this question



























  • just wondering, why on earth would you want to write it back even when there is no change applied in the file? why not just simply read it?

    – Bilal Ahmed
    Oct 17 at 7:27






  • 3





    @BilalAhmed: sure, it is a simplified test, the same empty line appear when applying changes to the string list

    – Fabrizio
    Oct 17 at 7:29











  • By "creates an empty line" I guess you mean that your original file does not end with the n character and the function adds the n to the file? Or does the function literally add a n right after an existing n at the end of file? POSIX requires text files to have all their lines terminated by a n, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminating n when you save files by default (e.g. vim, IDEs etc all by default make your files POSIX-compliant.)

    – Giacomo Alzetta
    Oct 17 at 15:49













8













8









8








I have a file .input.txt like this:



aaa
bbb
ccc


If I read it using TStrings.LoadFromFile and write it back (even without applying any changes) using TStrings.SaveToFile, it creates an empty line at the end of the output file.



var
Lines : TStrings;
begin
Lines := TStringList.Create;
try
Lines.LoadFromFile('.input.txt');

//...

Lines.SaveToFile('.output.txt');
finally
Lines.Free;
end;
end;


The same behavior can be observed using the TStrings.Text property which will return a string containing an empty line at its end.










share|improve this question
















I have a file .input.txt like this:



aaa
bbb
ccc


If I read it using TStrings.LoadFromFile and write it back (even without applying any changes) using TStrings.SaveToFile, it creates an empty line at the end of the output file.



var
Lines : TStrings;
begin
Lines := TStringList.Create;
try
Lines.LoadFromFile('.input.txt');

//...

Lines.SaveToFile('.output.txt');
finally
Lines.Free;
end;
end;


The same behavior can be observed using the TStrings.Text property which will return a string containing an empty line at its end.







delphi tstringlist






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Oct 17 at 12:27









Boann

41.1k13 gold badges94 silver badges125 bronze badges




41.1k13 gold badges94 silver badges125 bronze badges










asked Oct 17 at 7:23









FabrizioFabrizio

5,3134 gold badges22 silver badges55 bronze badges




5,3134 gold badges22 silver badges55 bronze badges















  • just wondering, why on earth would you want to write it back even when there is no change applied in the file? why not just simply read it?

    – Bilal Ahmed
    Oct 17 at 7:27






  • 3





    @BilalAhmed: sure, it is a simplified test, the same empty line appear when applying changes to the string list

    – Fabrizio
    Oct 17 at 7:29











  • By "creates an empty line" I guess you mean that your original file does not end with the n character and the function adds the n to the file? Or does the function literally add a n right after an existing n at the end of file? POSIX requires text files to have all their lines terminated by a n, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminating n when you save files by default (e.g. vim, IDEs etc all by default make your files POSIX-compliant.)

    – Giacomo Alzetta
    Oct 17 at 15:49

















  • just wondering, why on earth would you want to write it back even when there is no change applied in the file? why not just simply read it?

    – Bilal Ahmed
    Oct 17 at 7:27






  • 3





    @BilalAhmed: sure, it is a simplified test, the same empty line appear when applying changes to the string list

    – Fabrizio
    Oct 17 at 7:29











  • By "creates an empty line" I guess you mean that your original file does not end with the n character and the function adds the n to the file? Or does the function literally add a n right after an existing n at the end of file? POSIX requires text files to have all their lines terminated by a n, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminating n when you save files by default (e.g. vim, IDEs etc all by default make your files POSIX-compliant.)

    – Giacomo Alzetta
    Oct 17 at 15:49
















just wondering, why on earth would you want to write it back even when there is no change applied in the file? why not just simply read it?

– Bilal Ahmed
Oct 17 at 7:27





just wondering, why on earth would you want to write it back even when there is no change applied in the file? why not just simply read it?

– Bilal Ahmed
Oct 17 at 7:27




3




3





@BilalAhmed: sure, it is a simplified test, the same empty line appear when applying changes to the string list

– Fabrizio
Oct 17 at 7:29





@BilalAhmed: sure, it is a simplified test, the same empty line appear when applying changes to the string list

– Fabrizio
Oct 17 at 7:29













By "creates an empty line" I guess you mean that your original file does not end with the n character and the function adds the n to the file? Or does the function literally add a n right after an existing n at the end of file? POSIX requires text files to have all their lines terminated by a n, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminating n when you save files by default (e.g. vim, IDEs etc all by default make your files POSIX-compliant.)

– Giacomo Alzetta
Oct 17 at 15:49





By "creates an empty line" I guess you mean that your original file does not end with the n character and the function adds the n to the file? Or does the function literally add a n right after an existing n at the end of file? POSIX requires text files to have all their lines terminated by a n, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminating n when you save files by default (e.g. vim, IDEs etc all by default make your files POSIX-compliant.)

– Giacomo Alzetta
Oct 17 at 15:49












2 Answers
2






active

oldest

votes


















12



















For Delphi 10.1 and newer there is a property TrailingLineBreak controlling this behavior.




When TrailingLineBreak property is True (default value) then Text
property will contain line break after last line. When it is False,
then Text value will not contain line break after last line. This also
may be controlled by soTrailingLineBreak option.







share|improve this answer

























  • Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use the TrailingLineBreak property as soon as I upgrade the IDE. +1 and accepted

    – Fabrizio
    Oct 17 at 8:01



















1



















For Delphi 10.1 (Berlin) or newer, the best solution is described in Uwe's answer.



For older Delphi versions, I found a solution by creating a child class of TStringList and overriding the TStrings.GetTextStr virtual function but I will be glad to know if there is a better solution or if someone else found something wrong in my solution



Interface:



 uses
Classes;

type
TMyStringList = class(TStringList)
private
FIncludeLastLineBreakInText : Boolean;
protected
function GetTextStr: string; override;
public
constructor Create(AIncludeLastLineBreakInText : Boolean = False); overload;
property IncludeLastLineBreakInText : Boolean read FIncludeLastLineBreakInText write FIncludeLastLineBreakInText;
end;


Implementation:



uses
StrUtils;

constructor TMyStringList.Create(AIncludeLastLineBreakInText : Boolean = False);
begin
inherited Create;

FIncludeLastLineBreakInText := AIncludeLastLineBreakInText;
end;

function TMyStringList.GetTextStr: string;
begin
Result := inherited;

if(not IncludeLastLineBreakInText) and EndsStr(LineBreak, Result)
then SetLength(Result, Length(Result) - Length(LineBreak));
end;


Example:



procedure TForm1.Button1Click(Sender: TObject);
var
Lines : TStrings;
begin
Lines := TMyStringList.Create();
try
Lines.LoadFromFile('.input.txt');
Lines.SaveToFile('.output.txt');
finally
Lines.Free;
end;
end;





share|improve this answer






















  • 7





    It is worth pointing out that your code occasionally does SetLength(Result, -2).

    – Andreas Rejbrand
    Oct 17 at 7:30







  • 1





    In your GetTextStr, if Length(Result) is 0, then you do SetLength(Result, -2), which is bad. It might be the case that the effect is the same as SetLength(Result, 0), but I know of no guarantee regarding that. The official documentation, at least, doesn't contain any such guarantee. (So in theory bad things could happen.)

    – Andreas Rejbrand
    Oct 17 at 9:53






  • 2





    But now you still got another bug! If Length(Result) = 1, then you do SetLength(Result, -1), which is equally bad! In addition, it might be the case that Result doesn't end with a line break, in which case you will remove the two last characters from the last line. That's also a bug. (And that might happen, for instance, if you use TrailingLineBreak, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, like if not IncludeLastLineBreakInText and Result.EndsWith(LineBreak) then.

    – Andreas Rejbrand
    Oct 17 at 10:07







  • 1





    @AndreasRejbrand: I took it for granted that in presence of any char, the TStrings would add at least a LineBreak, but this behavior could change in future. Answer updated again, thanks

    – Fabrizio
    Oct 17 at 10:20






  • 1





    I'm sorry, but the new condition is still wrong... :( Pos(LineBreak, Result) = Length(Result) - Length(LineBreak) + 1. Pos gives the index of the first match. If you string contains 6 line breaks, it will give the position of the first one, but you clearly expect the last one...

    – Andreas Rejbrand
    Oct 17 at 10:31












Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);














draft saved

draft discarded
















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f58427050%2fhow-do-i-prevent-tstrings-savetofile-creating-a-final-empty-line%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown


























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









12



















For Delphi 10.1 and newer there is a property TrailingLineBreak controlling this behavior.




When TrailingLineBreak property is True (default value) then Text
property will contain line break after last line. When it is False,
then Text value will not contain line break after last line. This also
may be controlled by soTrailingLineBreak option.







share|improve this answer

























  • Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use the TrailingLineBreak property as soon as I upgrade the IDE. +1 and accepted

    – Fabrizio
    Oct 17 at 8:01
















12



















For Delphi 10.1 and newer there is a property TrailingLineBreak controlling this behavior.




When TrailingLineBreak property is True (default value) then Text
property will contain line break after last line. When it is False,
then Text value will not contain line break after last line. This also
may be controlled by soTrailingLineBreak option.







share|improve this answer

























  • Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use the TrailingLineBreak property as soon as I upgrade the IDE. +1 and accepted

    – Fabrizio
    Oct 17 at 8:01














12















12











12









For Delphi 10.1 and newer there is a property TrailingLineBreak controlling this behavior.




When TrailingLineBreak property is True (default value) then Text
property will contain line break after last line. When it is False,
then Text value will not contain line break after last line. This also
may be controlled by soTrailingLineBreak option.







share|improve this answer














For Delphi 10.1 and newer there is a property TrailingLineBreak controlling this behavior.




When TrailingLineBreak property is True (default value) then Text
property will contain line break after last line. When it is False,
then Text value will not contain line break after last line. This also
may be controlled by soTrailingLineBreak option.








share|improve this answer













share|improve this answer




share|improve this answer










answered Oct 17 at 7:51









Uwe RaabeUwe Raabe

37.1k2 gold badges71 silver badges110 bronze badges




37.1k2 gold badges71 silver badges110 bronze badges















  • Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use the TrailingLineBreak property as soon as I upgrade the IDE. +1 and accepted

    – Fabrizio
    Oct 17 at 8:01


















  • Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use the TrailingLineBreak property as soon as I upgrade the IDE. +1 and accepted

    – Fabrizio
    Oct 17 at 8:01

















Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use the TrailingLineBreak property as soon as I upgrade the IDE. +1 and accepted

– Fabrizio
Oct 17 at 8:01






Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use the TrailingLineBreak property as soon as I upgrade the IDE. +1 and accepted

– Fabrizio
Oct 17 at 8:01














1



















For Delphi 10.1 (Berlin) or newer, the best solution is described in Uwe's answer.



For older Delphi versions, I found a solution by creating a child class of TStringList and overriding the TStrings.GetTextStr virtual function but I will be glad to know if there is a better solution or if someone else found something wrong in my solution



Interface:



 uses
Classes;

type
TMyStringList = class(TStringList)
private
FIncludeLastLineBreakInText : Boolean;
protected
function GetTextStr: string; override;
public
constructor Create(AIncludeLastLineBreakInText : Boolean = False); overload;
property IncludeLastLineBreakInText : Boolean read FIncludeLastLineBreakInText write FIncludeLastLineBreakInText;
end;


Implementation:



uses
StrUtils;

constructor TMyStringList.Create(AIncludeLastLineBreakInText : Boolean = False);
begin
inherited Create;

FIncludeLastLineBreakInText := AIncludeLastLineBreakInText;
end;

function TMyStringList.GetTextStr: string;
begin
Result := inherited;

if(not IncludeLastLineBreakInText) and EndsStr(LineBreak, Result)
then SetLength(Result, Length(Result) - Length(LineBreak));
end;


Example:



procedure TForm1.Button1Click(Sender: TObject);
var
Lines : TStrings;
begin
Lines := TMyStringList.Create();
try
Lines.LoadFromFile('.input.txt');
Lines.SaveToFile('.output.txt');
finally
Lines.Free;
end;
end;





share|improve this answer






















  • 7





    It is worth pointing out that your code occasionally does SetLength(Result, -2).

    – Andreas Rejbrand
    Oct 17 at 7:30







  • 1





    In your GetTextStr, if Length(Result) is 0, then you do SetLength(Result, -2), which is bad. It might be the case that the effect is the same as SetLength(Result, 0), but I know of no guarantee regarding that. The official documentation, at least, doesn't contain any such guarantee. (So in theory bad things could happen.)

    – Andreas Rejbrand
    Oct 17 at 9:53






  • 2





    But now you still got another bug! If Length(Result) = 1, then you do SetLength(Result, -1), which is equally bad! In addition, it might be the case that Result doesn't end with a line break, in which case you will remove the two last characters from the last line. That's also a bug. (And that might happen, for instance, if you use TrailingLineBreak, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, like if not IncludeLastLineBreakInText and Result.EndsWith(LineBreak) then.

    – Andreas Rejbrand
    Oct 17 at 10:07







  • 1





    @AndreasRejbrand: I took it for granted that in presence of any char, the TStrings would add at least a LineBreak, but this behavior could change in future. Answer updated again, thanks

    – Fabrizio
    Oct 17 at 10:20






  • 1





    I'm sorry, but the new condition is still wrong... :( Pos(LineBreak, Result) = Length(Result) - Length(LineBreak) + 1. Pos gives the index of the first match. If you string contains 6 line breaks, it will give the position of the first one, but you clearly expect the last one...

    – Andreas Rejbrand
    Oct 17 at 10:31















1



















For Delphi 10.1 (Berlin) or newer, the best solution is described in Uwe's answer.



For older Delphi versions, I found a solution by creating a child class of TStringList and overriding the TStrings.GetTextStr virtual function but I will be glad to know if there is a better solution or if someone else found something wrong in my solution



Interface:



 uses
Classes;

type
TMyStringList = class(TStringList)
private
FIncludeLastLineBreakInText : Boolean;
protected
function GetTextStr: string; override;
public
constructor Create(AIncludeLastLineBreakInText : Boolean = False); overload;
property IncludeLastLineBreakInText : Boolean read FIncludeLastLineBreakInText write FIncludeLastLineBreakInText;
end;


Implementation:



uses
StrUtils;

constructor TMyStringList.Create(AIncludeLastLineBreakInText : Boolean = False);
begin
inherited Create;

FIncludeLastLineBreakInText := AIncludeLastLineBreakInText;
end;

function TMyStringList.GetTextStr: string;
begin
Result := inherited;

if(not IncludeLastLineBreakInText) and EndsStr(LineBreak, Result)
then SetLength(Result, Length(Result) - Length(LineBreak));
end;


Example:



procedure TForm1.Button1Click(Sender: TObject);
var
Lines : TStrings;
begin
Lines := TMyStringList.Create();
try
Lines.LoadFromFile('.input.txt');
Lines.SaveToFile('.output.txt');
finally
Lines.Free;
end;
end;





share|improve this answer






















  • 7





    It is worth pointing out that your code occasionally does SetLength(Result, -2).

    – Andreas Rejbrand
    Oct 17 at 7:30







  • 1





    In your GetTextStr, if Length(Result) is 0, then you do SetLength(Result, -2), which is bad. It might be the case that the effect is the same as SetLength(Result, 0), but I know of no guarantee regarding that. The official documentation, at least, doesn't contain any such guarantee. (So in theory bad things could happen.)

    – Andreas Rejbrand
    Oct 17 at 9:53






  • 2





    But now you still got another bug! If Length(Result) = 1, then you do SetLength(Result, -1), which is equally bad! In addition, it might be the case that Result doesn't end with a line break, in which case you will remove the two last characters from the last line. That's also a bug. (And that might happen, for instance, if you use TrailingLineBreak, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, like if not IncludeLastLineBreakInText and Result.EndsWith(LineBreak) then.

    – Andreas Rejbrand
    Oct 17 at 10:07







  • 1





    @AndreasRejbrand: I took it for granted that in presence of any char, the TStrings would add at least a LineBreak, but this behavior could change in future. Answer updated again, thanks

    – Fabrizio
    Oct 17 at 10:20






  • 1





    I'm sorry, but the new condition is still wrong... :( Pos(LineBreak, Result) = Length(Result) - Length(LineBreak) + 1. Pos gives the index of the first match. If you string contains 6 line breaks, it will give the position of the first one, but you clearly expect the last one...

    – Andreas Rejbrand
    Oct 17 at 10:31













1















1











1









For Delphi 10.1 (Berlin) or newer, the best solution is described in Uwe's answer.



For older Delphi versions, I found a solution by creating a child class of TStringList and overriding the TStrings.GetTextStr virtual function but I will be glad to know if there is a better solution or if someone else found something wrong in my solution



Interface:



 uses
Classes;

type
TMyStringList = class(TStringList)
private
FIncludeLastLineBreakInText : Boolean;
protected
function GetTextStr: string; override;
public
constructor Create(AIncludeLastLineBreakInText : Boolean = False); overload;
property IncludeLastLineBreakInText : Boolean read FIncludeLastLineBreakInText write FIncludeLastLineBreakInText;
end;


Implementation:



uses
StrUtils;

constructor TMyStringList.Create(AIncludeLastLineBreakInText : Boolean = False);
begin
inherited Create;

FIncludeLastLineBreakInText := AIncludeLastLineBreakInText;
end;

function TMyStringList.GetTextStr: string;
begin
Result := inherited;

if(not IncludeLastLineBreakInText) and EndsStr(LineBreak, Result)
then SetLength(Result, Length(Result) - Length(LineBreak));
end;


Example:



procedure TForm1.Button1Click(Sender: TObject);
var
Lines : TStrings;
begin
Lines := TMyStringList.Create();
try
Lines.LoadFromFile('.input.txt');
Lines.SaveToFile('.output.txt');
finally
Lines.Free;
end;
end;





share|improve this answer
















For Delphi 10.1 (Berlin) or newer, the best solution is described in Uwe's answer.



For older Delphi versions, I found a solution by creating a child class of TStringList and overriding the TStrings.GetTextStr virtual function but I will be glad to know if there is a better solution or if someone else found something wrong in my solution



Interface:



 uses
Classes;

type
TMyStringList = class(TStringList)
private
FIncludeLastLineBreakInText : Boolean;
protected
function GetTextStr: string; override;
public
constructor Create(AIncludeLastLineBreakInText : Boolean = False); overload;
property IncludeLastLineBreakInText : Boolean read FIncludeLastLineBreakInText write FIncludeLastLineBreakInText;
end;


Implementation:



uses
StrUtils;

constructor TMyStringList.Create(AIncludeLastLineBreakInText : Boolean = False);
begin
inherited Create;

FIncludeLastLineBreakInText := AIncludeLastLineBreakInText;
end;

function TMyStringList.GetTextStr: string;
begin
Result := inherited;

if(not IncludeLastLineBreakInText) and EndsStr(LineBreak, Result)
then SetLength(Result, Length(Result) - Length(LineBreak));
end;


Example:



procedure TForm1.Button1Click(Sender: TObject);
var
Lines : TStrings;
begin
Lines := TMyStringList.Create();
try
Lines.LoadFromFile('.input.txt');
Lines.SaveToFile('.output.txt');
finally
Lines.Free;
end;
end;






share|improve this answer















share|improve this answer




share|improve this answer








edited Oct 17 at 10:49

























answered Oct 17 at 7:23









FabrizioFabrizio

5,3134 gold badges22 silver badges55 bronze badges




5,3134 gold badges22 silver badges55 bronze badges










  • 7





    It is worth pointing out that your code occasionally does SetLength(Result, -2).

    – Andreas Rejbrand
    Oct 17 at 7:30







  • 1





    In your GetTextStr, if Length(Result) is 0, then you do SetLength(Result, -2), which is bad. It might be the case that the effect is the same as SetLength(Result, 0), but I know of no guarantee regarding that. The official documentation, at least, doesn't contain any such guarantee. (So in theory bad things could happen.)

    – Andreas Rejbrand
    Oct 17 at 9:53






  • 2





    But now you still got another bug! If Length(Result) = 1, then you do SetLength(Result, -1), which is equally bad! In addition, it might be the case that Result doesn't end with a line break, in which case you will remove the two last characters from the last line. That's also a bug. (And that might happen, for instance, if you use TrailingLineBreak, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, like if not IncludeLastLineBreakInText and Result.EndsWith(LineBreak) then.

    – Andreas Rejbrand
    Oct 17 at 10:07







  • 1





    @AndreasRejbrand: I took it for granted that in presence of any char, the TStrings would add at least a LineBreak, but this behavior could change in future. Answer updated again, thanks

    – Fabrizio
    Oct 17 at 10:20






  • 1





    I'm sorry, but the new condition is still wrong... :( Pos(LineBreak, Result) = Length(Result) - Length(LineBreak) + 1. Pos gives the index of the first match. If you string contains 6 line breaks, it will give the position of the first one, but you clearly expect the last one...

    – Andreas Rejbrand
    Oct 17 at 10:31












  • 7





    It is worth pointing out that your code occasionally does SetLength(Result, -2).

    – Andreas Rejbrand
    Oct 17 at 7:30







  • 1





    In your GetTextStr, if Length(Result) is 0, then you do SetLength(Result, -2), which is bad. It might be the case that the effect is the same as SetLength(Result, 0), but I know of no guarantee regarding that. The official documentation, at least, doesn't contain any such guarantee. (So in theory bad things could happen.)

    – Andreas Rejbrand
    Oct 17 at 9:53






  • 2





    But now you still got another bug! If Length(Result) = 1, then you do SetLength(Result, -1), which is equally bad! In addition, it might be the case that Result doesn't end with a line break, in which case you will remove the two last characters from the last line. That's also a bug. (And that might happen, for instance, if you use TrailingLineBreak, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, like if not IncludeLastLineBreakInText and Result.EndsWith(LineBreak) then.

    – Andreas Rejbrand
    Oct 17 at 10:07







  • 1





    @AndreasRejbrand: I took it for granted that in presence of any char, the TStrings would add at least a LineBreak, but this behavior could change in future. Answer updated again, thanks

    – Fabrizio
    Oct 17 at 10:20






  • 1





    I'm sorry, but the new condition is still wrong... :( Pos(LineBreak, Result) = Length(Result) - Length(LineBreak) + 1. Pos gives the index of the first match. If you string contains 6 line breaks, it will give the position of the first one, but you clearly expect the last one...

    – Andreas Rejbrand
    Oct 17 at 10:31







7




7





It is worth pointing out that your code occasionally does SetLength(Result, -2).

– Andreas Rejbrand
Oct 17 at 7:30






It is worth pointing out that your code occasionally does SetLength(Result, -2).

– Andreas Rejbrand
Oct 17 at 7:30





1




1





In your GetTextStr, if Length(Result) is 0, then you do SetLength(Result, -2), which is bad. It might be the case that the effect is the same as SetLength(Result, 0), but I know of no guarantee regarding that. The official documentation, at least, doesn't contain any such guarantee. (So in theory bad things could happen.)

– Andreas Rejbrand
Oct 17 at 9:53





In your GetTextStr, if Length(Result) is 0, then you do SetLength(Result, -2), which is bad. It might be the case that the effect is the same as SetLength(Result, 0), but I know of no guarantee regarding that. The official documentation, at least, doesn't contain any such guarantee. (So in theory bad things could happen.)

– Andreas Rejbrand
Oct 17 at 9:53




2




2





But now you still got another bug! If Length(Result) = 1, then you do SetLength(Result, -1), which is equally bad! In addition, it might be the case that Result doesn't end with a line break, in which case you will remove the two last characters from the last line. That's also a bug. (And that might happen, for instance, if you use TrailingLineBreak, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, like if not IncludeLastLineBreakInText and Result.EndsWith(LineBreak) then.

– Andreas Rejbrand
Oct 17 at 10:07






But now you still got another bug! If Length(Result) = 1, then you do SetLength(Result, -1), which is equally bad! In addition, it might be the case that Result doesn't end with a line break, in which case you will remove the two last characters from the last line. That's also a bug. (And that might happen, for instance, if you use TrailingLineBreak, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, like if not IncludeLastLineBreakInText and Result.EndsWith(LineBreak) then.

– Andreas Rejbrand
Oct 17 at 10:07





1




1





@AndreasRejbrand: I took it for granted that in presence of any char, the TStrings would add at least a LineBreak, but this behavior could change in future. Answer updated again, thanks

– Fabrizio
Oct 17 at 10:20





@AndreasRejbrand: I took it for granted that in presence of any char, the TStrings would add at least a LineBreak, but this behavior could change in future. Answer updated again, thanks

– Fabrizio
Oct 17 at 10:20




1




1





I'm sorry, but the new condition is still wrong... :( Pos(LineBreak, Result) = Length(Result) - Length(LineBreak) + 1. Pos gives the index of the first match. If you string contains 6 line breaks, it will give the position of the first one, but you clearly expect the last one...

– Andreas Rejbrand
Oct 17 at 10:31





I'm sorry, but the new condition is still wrong... :( Pos(LineBreak, Result) = Length(Result) - Length(LineBreak) + 1. Pos gives the index of the first match. If you string contains 6 line breaks, it will give the position of the first one, but you clearly expect the last one...

– Andreas Rejbrand
Oct 17 at 10:31


















draft saved

draft discarded















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f58427050%2fhow-do-i-prevent-tstrings-savetofile-creating-a-final-empty-line%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown









Popular posts from this blog

Canceling a color specificationRandomly assigning color to Graphics3D objects?Default color for Filling in Mathematica 9Coloring specific elements of sets with a prime modified order in an array plotHow to pick a color differing significantly from the colors already in a given color list?Detection of the text colorColor numbers based on their valueCan color schemes for use with ColorData include opacity specification?My dynamic color schemes

Invision Community Contents History See also References External links Navigation menuProprietaryinvisioncommunity.comIPS Community ForumsIPS Community Forumsthis blog entry"License Changes, IP.Board 3.4, and the Future""Interview -- Matt Mecham of Ibforums""CEO Invision Power Board, Matt Mecham Is a Liar, Thief!"IPB License Explanation 1.3, 1.3.1, 2.0, and 2.1ArchivedSecurity Fixes, Updates And Enhancements For IPB 1.3.1Archived"New Demo Accounts - Invision Power Services"the original"New Default Skin"the original"Invision Power Board 3.0.0 and Applications Released"the original"Archived copy"the original"Perpetual licenses being done away with""Release Notes - Invision Power Services""Introducing: IPS Community Suite 4!"Invision Community Release Notes

199年 目錄 大件事 到箇年出世嗰人 到箇年死嗰人 節慶、風俗習慣 導覽選單