Làm cách nào để ngăn TStrings.SaveToFile tạo một dòng trống cuối cùng?


8

Tôi có một tập tin .\input.txtnhư thế này:

aaa
bbb
ccc

Nếu tôi đọc nó bằng cách sử dụng TStrings.LoadFromFilevà viết lại (ngay cả khi không áp dụng bất kỳ thay đổi nào) TStrings.SaveToFile, nó sẽ tạo ra một dòng trống ở cuối tệp đầu ra.

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

    //...

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

Hành vi tương tự có thể được quan sát bằng cách sử dụng thuộc TStrings.Texttính sẽ trả về một chuỗi chứa một dòng trống ở cuối.


chỉ tự hỏi, tại sao bạn muốn viết lại nó ngay cả khi không có thay đổi nào được áp dụng trong tập tin? Tại sao không chỉ đơn giản là đọc nó?
Bilal Ahmed

3
@BilalAhmed: chắc chắn, đó là một thử nghiệm đơn giản hóa, cùng một dòng trống xuất hiện khi áp dụng các thay đổi cho danh sách chuỗi
Fabrizio

Bằng cách "tạo một dòng trống" Tôi đoán bạn có nghĩa là tệp gốc của bạn không kết thúc bằng \nký tự và hàm thêm \ntệp vào tệp? Hoặc là chức năng thực sự thêm một \nquyền sau khi tồn \ntại ở cuối tập tin? POSIX yêu cầu các tệp văn bản phải có tất cả các dòng của chúng bị chấm dứt bởi a \n, chỉ fyi. Rất nhiều phần mềm được viết để làm theo một số tiêu chuẩn để lý do tại sao rất nhiều biên tập viên sẽ bổ sung chấm dứt thiếu \nkhi bạn lưu các tập tin theo mặc định (ví dụ vim, IDE vv tất cả theo mặc định làm cho tập tin của bạn POSIX-compliant.)
Giacomo Alzetta

Câu trả lời:


12

Đối với Delphi 10.1 và mới hơn, có một thuộc tính TrailingLineBreakkiểm soát hành vi này.

Khi thuộc tính TrailingLineBreak là True (giá trị mặc định) thì thuộc tính Text sẽ chứa ngắt dòng sau dòng cuối cùng. Khi nó là Sai, thì giá trị Văn bản sẽ không chứa ngắt dòng sau dòng cuối cùng. Điều này cũng có thể được kiểm soát bởi tùy chọn soTrailingLineBreak.


Thông tin tuyệt vời, tôi đang làm việc trên Delphi2007 và DelphiXE7 nhưng tôi chắc chắn sẽ rất vui khi sử dụng TrailingLineBreaktài sản ngay khi tôi nâng cấp IDE. +1 và được chấp nhận
Fabrizio

1

Đối với Delphi 10.1 (Berlin) hoặc mới hơn, giải pháp tốt nhất được mô tả trong câu trả lời của Uwe.

Đối với các phiên bản Delphi cũ hơn, tôi đã tìm ra giải pháp bằng cách tạo một lớp con TStringListvà ghi đè TStrings.GetTextStrhàm ảo nhưng tôi sẽ vui mừng khi biết liệu có giải pháp nào tốt hơn hoặc nếu có ai đó tìm thấy lỗi trong giải pháp của tôi

Giao diện:

  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;

Thực hiện:

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;

Thí dụ:

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
Đó là giá trị chỉ ra rằng mã của bạn đôi khi làm SetLength(Result, -2).
Andreas Rejbrand

1
Trong của bạn GetTextStr, nếu Length(Result)0, sau đó bạn làm SetLength(Result, -2), đó là xấu. Nó có thể là trường hợp mà hiệu ứng là như nhau SetLength(Result, 0), nhưng tôi biết không có gì đảm bảo về điều đó. Các tài liệu chính thức, ít nhất, không chứa bất kỳ đảm bảo như vậy. (Vì vậy, trên lý thuyết những điều tồi tệ có thể xảy ra.)
Andreas Rejbrand

2
Nhưng bây giờ bạn vẫn có một lỗi khác! Nếu Length(Result) = 1, sau đó bạn làm SetLength(Result, -1), đó là xấu như nhau! Ngoài ra, đó có thể là trường hợp Resultkhông kết thúc bằng ngắt dòng, trong trường hợp đó bạn sẽ xóa hai ký tự cuối cùng khỏi dòng cuối cùng. Đó cũng là một lỗi. (Và điều đó có thể xảy ra, ví dụ, nếu bạn sử dụng TrailingLineBreak, tôi nghi ngờ. Ngay cả khi không, có thể có các trường hợp khác.) Bạn thực sự nên kiểm tra xem chuỗi có thực sự kết thúc bằng ngắt dòng hay không, như thế nào if not IncludeLastLineBreakInText and Result.EndsWith(LineBreak) then.
Andreas Rejbrand

1
@AndreasRejbrand: Tôi đã chấp nhận rằng với sự có mặt của bất kỳ char nào, TStrings sẽ thêm ít nhất một LineBreak, nhưng hành vi này có thể thay đổi trong tương lai. Câu trả lời được cập nhật một lần nữa, cảm ơn
Fabrizio

1
Tôi xin lỗi, nhưng điều kiện mới vẫn sai ... :( Pos(LineBreak, Result) = Length(Result) - Length(LineBreak) + 1. PosĐưa ra chỉ số của trận đấu đầu tiên. Nếu chuỗi của bạn chứa 6 ngắt dòng, nó sẽ đưa ra vị trí của lần đầu tiên, nhưng bạn rõ ràng mong đợi lần cuối một ...
Andreas Rejbrand
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.