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;
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
add a comment
|
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
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 then
character and the function adds then
to the file? Or does the function literally add an
right after an existingn
at the end of file? POSIX requires text files to have all their lines terminated by an
, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminatingn
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
add a comment
|
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
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
delphi tstringlist
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 then
character and the function adds then
to the file? Or does the function literally add an
right after an existingn
at the end of file? POSIX requires text files to have all their lines terminated by an
, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminatingn
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
add a comment
|
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 then
character and the function adds then
to the file? Or does the function literally add an
right after an existingn
at the end of file? POSIX requires text files to have all their lines terminated by an
, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminatingn
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
add a comment
|
2 Answers
2
active
oldest
votes
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.
Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use theTrailingLineBreak
property as soon as I upgrade the IDE. +1 and accepted
– Fabrizio
Oct 17 at 8:01
add a comment
|
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;
7
It is worth pointing out that your code occasionally doesSetLength(Result, -2)
.
– Andreas Rejbrand
Oct 17 at 7:30
1
In yourGetTextStr
, ifLength(Result)
is0
, then you doSetLength(Result, -2)
, which is bad. It might be the case that the effect is the same asSetLength(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! IfLength(Result) = 1
, then you doSetLength(Result, -1)
, which is equally bad! In addition, it might be the case thatResult
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 useTrailingLineBreak
, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, likeif 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
|
show 6 more comments
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use theTrailingLineBreak
property as soon as I upgrade the IDE. +1 and accepted
– Fabrizio
Oct 17 at 8:01
add a comment
|
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.
Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use theTrailingLineBreak
property as soon as I upgrade the IDE. +1 and accepted
– Fabrizio
Oct 17 at 8:01
add a comment
|
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.
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.
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 theTrailingLineBreak
property as soon as I upgrade the IDE. +1 and accepted
– Fabrizio
Oct 17 at 8:01
add a comment
|
Great information, I'm working on Delphi2007 and DelphiXE7 but I'll surely be glad to use theTrailingLineBreak
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
add a comment
|
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;
7
It is worth pointing out that your code occasionally doesSetLength(Result, -2)
.
– Andreas Rejbrand
Oct 17 at 7:30
1
In yourGetTextStr
, ifLength(Result)
is0
, then you doSetLength(Result, -2)
, which is bad. It might be the case that the effect is the same asSetLength(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! IfLength(Result) = 1
, then you doSetLength(Result, -1)
, which is equally bad! In addition, it might be the case thatResult
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 useTrailingLineBreak
, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, likeif 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
|
show 6 more comments
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;
7
It is worth pointing out that your code occasionally doesSetLength(Result, -2)
.
– Andreas Rejbrand
Oct 17 at 7:30
1
In yourGetTextStr
, ifLength(Result)
is0
, then you doSetLength(Result, -2)
, which is bad. It might be the case that the effect is the same asSetLength(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! IfLength(Result) = 1
, then you doSetLength(Result, -1)
, which is equally bad! In addition, it might be the case thatResult
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 useTrailingLineBreak
, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, likeif 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
|
show 6 more comments
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;
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;
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 doesSetLength(Result, -2)
.
– Andreas Rejbrand
Oct 17 at 7:30
1
In yourGetTextStr
, ifLength(Result)
is0
, then you doSetLength(Result, -2)
, which is bad. It might be the case that the effect is the same asSetLength(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! IfLength(Result) = 1
, then you doSetLength(Result, -1)
, which is equally bad! In addition, it might be the case thatResult
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 useTrailingLineBreak
, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, likeif 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
|
show 6 more comments
7
It is worth pointing out that your code occasionally doesSetLength(Result, -2)
.
– Andreas Rejbrand
Oct 17 at 7:30
1
In yourGetTextStr
, ifLength(Result)
is0
, then you doSetLength(Result, -2)
, which is bad. It might be the case that the effect is the same asSetLength(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! IfLength(Result) = 1
, then you doSetLength(Result, -1)
, which is equally bad! In addition, it might be the case thatResult
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 useTrailingLineBreak
, I suspect. Even if not, there might be other instances.) You really should test if the string really ends with a line break, likeif 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
|
show 6 more comments
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 then
to the file? Or does the function literally add an
right after an existingn
at the end of file? POSIX requires text files to have all their lines terminated by an
, just fyi. Lots of software was written to follow some standards so that's why a lot of editors will add the missing terminatingn
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