12

我可以将writeln数字格式化为一行文本。

var 
  file: text;
  mystring: string;

begin
  writeln(file,'Result is: ', var1:8:2,' | ', var2:8:2,' |');
end;

在 Delphi 中是否有类似的易于使用的程序来处理类似的结果

  _format_string(mystring, 'Result is: ', var1:8:2,' | ', var2:8:2,' |');

谢谢你。

4

4 回答 4

17

您使用以下Format功能:

mystring := Format('The image dimensions are %d × %d.', [640, 480]);
于 2013-08-01T12:31:37.077 回答
12

从技术上讲,答案是“是”。但不建议这样做。

您可以根据System.TTextRec类型编写自己的文本文件设备驱动程序。

我过去曾这样做过(尤其是在 Turbo Pascal 时代),用于调试目的。这是很多工作,并且需要您编写一个特殊的MyAssign程序,并确保使用块中的关闭Text文件。麻烦,但可行。TTextRectry..finally

一个更简单的替代方法是使用 Andreas Rejbrand 描述的 Format 函数

使用的很酷的一点Format是,您使用Format Strings不仅可以参数化诸如widthprecision内部的内容Format String,而且还可以像您通常一样提供值的参数。

您可以非常接近使用width8 和precision2 的 a,就像您问题中的示例一样。

例如,引用文档:

Format ('%*.*f', [8, 2, 123.456]);

相当于:

Format ('%8.2f', [123.456]);

Format这是and的一个很容易被忽视的特性Format Strings

于 2013-08-01T15:29:34.663 回答
6

尽管 Jeroen 不推荐它,但我大约一年前做过类似的事情——只是为了学习如何去做。这是代码:

type
  TTextFile = class
  private type
    TTextRecHelper = record helper for TTextRec
    public
      function GetTextFile: TTextFile;
      procedure SetTextFile(const Value: TTextFile);
      property TextFile: TTextFile read GetTextFile write SetTextFile;
    end;
  private var
    FBuilder: TStringBuilder;
    class function TextClose(var F: TTextRec): Integer; static;
    class function TextIgnore(var F: TTextRec): Integer; static;
    class function TextInput(var F: TTextRec): Integer; static;
    class function TextOpen(var F: TTextRec): Integer; static;
    class function TextOutput(var F: TTextRec): Integer; static;
    procedure AppendString(const Value: string);
    procedure AssignFile(var F: Text);
  public
    var F: Text;
    constructor Create;
    destructor Destroy; override;
    function ToString: string; override;
  end;

constructor TTextFile.Create;
begin
  inherited Create;
  FBuilder := TStringBuilder.Create();
  AssignFile(F);
  Rewrite(F);
end;

destructor TTextFile.Destroy;
begin
  Close(F);
  FBuilder.Free;
  inherited Destroy;
end;

procedure TTextFile.AppendString(const Value: string);
begin
  FBuilder.Append(Value);
end;

procedure TTextFile.AssignFile(var F: Text);
begin
  FillChar(F, SizeOf(F), 0);
  with TTextRec(F)do
  begin
    Mode := fmClosed;
    BufSize := SizeOf(Buffer);
    BufPtr := @Buffer;
    OpenFunc := @TextOpen;
    TextFile := Self;
  end;
end;

class function TTextFile.TextClose(var F: TTextRec): Integer;
begin
  Result := 0;
end;

class function TTextFile.TextIgnore(var F: TTextRec): Integer;
begin
  Result := 0;
end;

class function TTextFile.TextInput(var F: TTextRec): Integer;
begin
  F.BufPos := 0;
  F.BufEnd := 0;
  Result := 0;
end;

class function TTextFile.TextOpen(var F: TTextRec): Integer;
begin
  if F.Mode = fmInput then
  begin
    F.InOutFunc := @TextInput;
    F.FlushFunc := @TextIgnore;
    F.CloseFunc := @TextIgnore;
  end else
  begin
    F.Mode := fmOutput;
    F.InOutFunc := @TextOutput;
    F.FlushFunc := @TextOutput;
    F.CloseFunc := @TextClose;
  end;
  Result := 0;
end;

class function TTextFile.TextOutput(var F: TTextRec): Integer;
var
  AStr: AnsiString;
begin
  SetLength(AStr, F.BufPos);
  Move(F.BufPtr^, AStr[1], F.BufPos);
  F.TextFile.AppendString(string(AStr));
  F.BufPos := 0;
  Result := 0;
end;

function TTextFile.ToString: string;
begin
  Close(F);
  result := FBuilder.ToString;
  Rewrite(F);
end;

function TTextFile.TTextRecHelper.GetTextFile: TTextFile;
begin
  Move(UserData[1], Result, Sizeof(Result));
end;

procedure TTextFile.TTextRecHelper.SetTextFile(const Value: TTextFile);
begin
  Move(Value, UserData[1], Sizeof(Value));
end;

根据您的问题如何使用它的示例:

  tf := TTextFile.Create;
  try
    Writeln(tf.F, 'Result is: ', var1:8:2,' | ', var2:8:2,' |');
    Caption := tf.ToString;
  finally
    tf.Free;
  end;
于 2013-08-01T17:12:43.593 回答
2

关于 Writeln 的注意事项

Writeln(file, 'Result is: ', var1:8:2,' | ', var2:8:2,' |');

输出:

Result is:     4.50 |     0.67 |

似乎 Delphi 在不尊重DecimalSeparator的情况下执行旧的 Pascal 格式。这就是为什么Writeln使用输出.和我下面使用的其他方法的原因,(我有一个西班牙语版本的 Windows)。

TStringBuilder

在现代 Delphi 版本中,TStringBuilder提供了一种优雅的字符串连接方式,支持流畅的接口。它的格式化功能有限,但包含一种Format风格(作为常规Format函数,它非常有用,但缺少类型检查):

sb := TStringBuilder.Create;
try       
  sb.Append('Result is: ').Append(var1).Append(' | ').Append(var2).Append(' |');
  Memo.Lines.Add(sb.ToString);
  sb.Clear;
  sb.AppendFormat('Result is: %8.2f | %8.2f |', [var1, var2]);
  Memo.Lines.Add(sb.ToString);
finally                     
  sb.Free;
end;

输出:

Result is: 4,5 | 0,666666666666667 |
Result is:     4,50 |     0,67 |

插入运算符

使用一些技巧,例如运算符重载和闭包,可以模仿C++ ostream 插入运算符


Memo.Lines.Add(stringout < 'My ' < 5 < ' cents' < soEndl < '2/3: ' < soPrec(4) < 2/3);

输出:

My 5 cents  
2/3: 0,6667

你的例子:

Memo.Lines.Add(
  stringout
    < 'Result is: ' < soWidth(8) < soPrec(2) < var1 < ' | '
    < soWidth(8) < soPrec(2) < var2 < ' |'
);

输出:

Result is:     4,50 |     0,67 |

当 Delphi 在类中支持运算符重载时,实现会更加简洁。同时,使用记录进行运算符重载和接口进行自动内存管理可以解决问题:

type
  PStringOut = ^TStringOut;

  TStringOutManipulatorRef = reference to procedure(pso: PStringOut);

  PStringOutInternalStorage = ^TStringOutInternalStorage;

  TStringOutInternalStorage = record
    Data: TStringBuilder;
    Width, Precision: integer;
    procedure ClearFormat; inline;
    function GetFormatString(formatType: char): string;
  end;

  IStringOutInternal = interface
    function TheStorage: PStringOutInternalStorage;
  end;

  TStringOutInternal = class(TInterfacedObject, IStringOutInternal)
  strict private
    Storage: TStringOutInternalStorage;
  private
    constructor Create;
    function TheStorage: PStringOutInternalStorage;
  public
    destructor Destroy; override;
  end;

  TStringOut = record
  private
    Buffer: IStringOutInternal;
  public
    // insertion operator
    class operator LessThan(const this: TStringOut; add: string): TStringOut;
    class operator LessThan(const this: TStringOut; add: char): TStringOut;
    class operator LessThan(const this: TStringOut; add: integer): TStringOut;
    class operator LessThan(const this: TStringOut; add: double): TStringOut;
    class operator LessThan(const this: TStringOut; manipulator: TStringOutManipulatorRef): TStringOut; inline;

    // implicit conversion to string ("extraction" operator)
    class operator Implicit(const this: TStringOut): string; inline;
  end;

{ TStringOutInternalStorage }

procedure TStringOutInternalStorage.ClearFormat;
begin
  Width := 0;
  Precision := 0;
end;

function TStringOutInternalStorage.GetFormatString(formatType: char): string;
begin
  Result := '%';
  if Width > 0 then
    Result := Result + IntToStr(Width);
  if Precision > 0 then
    Result := Result + '.' + IntToStr(Precision);
  Result := Result + formatType;
end;

{ TStringOutInternal }

constructor TStringOutInternal.Create;
begin
  inherited;
  Storage.Data := TStringBuilder.Create;
end;

destructor TStringOutInternal.Destroy;
begin
  Storage.Data.Free;
  inherited;
end;

function TStringOutInternal.TheStorage: PStringOutInternalStorage;
begin
  Result := @Storage;
end;

{ TStringOut }

class operator TStringOut.Implicit(const this: TStringOut): string;
begin
  Result := this.Buffer.TheStorage.Data.ToString;
end;

class operator TStringOut.LessThan(const this: TStringOut; add: string): TStringOut;
begin
  this.Buffer.TheStorage.Data.AppendFormat(this.Buffer.TheStorage.GetFormatString('s'), [add]);
  this.Buffer.TheStorage.ClearFormat;
  Result.Buffer := this.Buffer;
end;

class operator TStringOut.LessThan(const this: TStringOut; add: char): TStringOut;
begin
  this.Buffer.TheStorage.Data.Append(add);
  this.Buffer.TheStorage.ClearFormat;
  Result.Buffer := this.Buffer;
end;

class operator TStringOut.LessThan(const this: TStringOut; add: integer): TStringOut;
begin
  this.Buffer.TheStorage.Data.AppendFormat(this.Buffer.TheStorage.GetFormatString('d'), [add]);
  this.Buffer.TheStorage.ClearFormat;
  Result.Buffer := this.Buffer;
end;

class operator TStringOut.LessThan(const this: TStringOut; add: double): TStringOut;
var
  s: PStringOutInternalStorage;
begin
  s := this.Buffer.TheStorage;

  if s.Precision <> 0
  then s.Data.AppendFormat(s.GetFormatString('f'), [add])
  else s.Data.AppendFormat(s.GetFormatString('g'), [add]);

  s.ClearFormat;
  Result.Buffer := this.Buffer;
end;

class operator TStringOut.LessThan(const this: TStringOut; manipulator: TStringOutManipulatorRef): TStringOut;
begin
  Result := this;
  manipulator(@Result);
end;

{ Manipulators }

function soEndl: TStringOutManipulatorRef;
begin
  Result :=
    procedure(pso: PStringOut)
    begin
      pso.Buffer.TheStorage.Data.AppendLine;
      pso.Buffer.TheStorage.ClearFormat;
    end;
end;

function soWidth(value: integer): TStringOutManipulatorRef;
begin
  Result :=
    procedure(pso: PStringOut)
    begin
      pso.Buffer.TheStorage.Width := value;
    end;
end;

function soPrec(value: integer): TStringOutManipulatorRef;
begin
  Result :=
    procedure(pso: PStringOut)
    begin
      pso.Buffer.TheStorage.Precision := value;
    end;
end;

{ The stringout "constructor" }

function stringout: TStringOut; inline;
begin
  Result.Buffer := TStringOutInternal.Create;
end;
于 2013-08-01T21:20:25.513 回答