TStrings.SaveToFileが最後の空の行を作成しないようにするにはどうすればよいですか?


8

次の.\input.txtようなファイルがあります。

aaa
bbb
ccc

を使用TStrings.LoadFromFileしてそれを読み取り、それを(変更を適用せずに)を使用して書き戻すTStrings.SaveToFileと、出力ファイルの最後に空の行が作成されます。

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

    //...

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

同じ動作がTStrings.Text、最後に空行を含む文字列を返すプロパティを使用して観察できます。


ファイルに変更が適用されていないのに、いったいなぜそれを書き戻したいのでしょうか。なぜそれを単に読むだけではないのですか?
ビラルアーメド

3
@BilalAhmed:確かに、これは簡略化されたテストです。文字列リストに変更を適用すると、同じ空の行が表示されます
Fabrizio

「空の行を作成する」とは、元のファイルが\n文字で終わっておらず、関数が\nファイルに追加することを意味していると思いますか?または、関数は文字列の末尾に\n存在する文字の直後に文字通り右を追加し\nますか?POSIXでは、テキストファイルのすべての行を\n、fyiで終了させる必要があります。ソフトウェアの多くの編集者の多くは終了不足しているが追加されます理由ですので、いくつかの基準に従うために書かれた\nあなたは、デフォルトでファイルを保存する場合(例えばvim、デフォルトでのIDEなど、すべてのPOSIX準拠のファイルを作成します。)
ジャコモAlzetta

回答:


12

Delphi 10.1以降には、TrailingLineBreakこの動作を制御するプロパティがあります。

TrailingLineBreakプロパティがTrue(デフォルト値)の場合、Textプロパティには最後の行の後に改行が含まれます。Falseの場合、テキスト値には最後の行の後に改行が含まれません。これは、soTrailingLineBreakオプションでも制御できます。


すばらしい情報です。私はDelphi2007とDelphiXE7に取り組んでいTrailingLineBreakますが、IDEをアップグレードしたらすぐにこのプロパティを使用できることを嬉しく思います。+1と承認
Fabrizio

1

Delphi 10.1(ベルリン)以降の場合、最良のソリューションはUweの回答に記載されています。

古いDelphiバージョンの場合、私TStringListTStrings.GetTextStr仮想関数の子クラスを作成してオーバーライドすることで解決策を見つけましたが、より良い解決策があるかどうか、または誰かが私の解決策で何か問題を見つけたかどうかを知りたいと思います

インターフェース:

  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;

実装:

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;

例:

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
あなたのコードが時々行うことを指摘する価値がありますSetLength(Result, -2)
Andreas Rejbrand

1
あなたは、でGetTextStrあれば、Length(Result)である0、あなたが行うSetLength(Result, -2)、悪いです。効果はと同じかもしれませんが、それSetLength(Result, 0)について保証はありません。少なくとも公式文書にはそのような保証は含まれていません。(したがって、理論的には悪いことが起こる可能性があります。)
Andreas Rejbrand

2
しかし、今でもまだ別のバグがあります!もしそうならLength(Result) = 1、あなたはそうしますSetLength(Result, -1)、それは同様に悪いです!さらに、Result改行で終わらない場合もあります。その場合、最後の行から最後の2つの文字を削除します。それもバグです。(たとえば、これを使用するとTrailingLineBreak、たとえば、を使用した場合に発生する可能性があります。そうでない場合でも、他のインスタンスが存在する可能性があります。)実際に、文字列がのように改行で終了しているかどうかをテストする必要がありますif not IncludeLastLineBreakInText and Result.EndsWith(LineBreak) then
Andreas Rejbrand

1
@AndreasRejbrand:任意の文字が存在する場合、TStringsは少なくともLineBreakを追加することを当然のことと考えていましたが、この動作は将来変更される可能性があります。回答の更新、ありがとう
Fabrizio

1
申し訳ありませんが、新しい条件はまだ間違っています... :( Pos(LineBreak, Result) = Length(Result) - Length(LineBreak) + 1Pos最初の一致のインデックスを示します。文字列に6つの改行が含まれている場合、最初の改行の位置が表示されますが、最後の行が予想されます1つ...
Andreas Rejbrand
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.