【问题标题】:Delphi - tfilestream: write time and date to fileDelphi - tfilestream:将时间和日期写入文件
【发布时间】:2015-04-14 20:48:16
【问题描述】:

我正在尝试为每个事件保存一行,其中包含一段文本和发生的时间 + 日期。

问题是:

  1. 时间显示为中文字体
  2. 它一遍又一遍地替换同一行

代码如下:

    uses sysUtils, classes;

function log: Boolean;
var
  fs: TFileStream;
  i : String;
  time : TDateTime;

begin
  i := 'Boss is dead!';
  time := now;
  try
    fs := TFileStream.Create('log.txt', fmCreate or fmOpenWrite);
    fs.Write(PChar(i +TimeToStr(time))^, Length(i +TimeToStr(time)));
    fs.write(i, sizeof(i));   
  finally
    fs.Free; 
  end;

end;

谢谢。

【问题讨论】:

    标签: delphi tfilestream


    【解决方案1】:

    通常当您期望拉丁文本但看到中文文本时,这意味着您将 ANSI 文本解释为 UTF-16。由此我推断您打算写出 UTF-16 文本,但实际上是写出 ANSI 文本。这意味着你有一个 pre-Unicode Delphi。

    至于为什么你一直覆盖文件,那是因为你传递了fmCreate。您真正想要做的是以写入模式打开现有文件,或者如果不存在文件,则创建一个新文件。 Win32 API 函数支持 OPEN_ALWAYS 创建处置。但这不能通过TFileStream.Create 获得。所以你必须使用THandleStream并直接调用CreateFile。请注意,较新版本的 Delphi 引入了 TFile.Open,它公开了 OPEN_ALWAYS 功能。但我的推理是你使用的 Delphi 太旧了。

    假设你可以使用THandleStream,并正确调用CreateFile,那么你只需要写出UTF-16文本。最简单的方法是使用WideString。你可以这样写代码:

    var
      ws: WideString;
    ....
    Stream.Seek(0, soFromEnd);
    ws := TimeToStr(Now) + sLineBreak;
    Stream.WriteBuffer(PWideChar(ws)^, SizeOf(WideChar)*Length(ws));
    

    另一方面,也许中国人来自这里:

    fs.write(i, sizeof(i));
    

    这段代码只是写了一个指向文件的指针。您肯定不想这样做,应该删除该行。为了完整起见,如果您确实有 Unicode Delphi,那么您将以非常相似的方式编写文本。像这样:

    var
      s: string;
    ....
    Stream.Seek(0, soFromEnd);
    s := TimeToStr(Now) + sLineBreak;
    Stream.WriteBuffer(PWideChar(s)^, SizeOf(WideChar)*Length(s));
    

    回到附加到流中,它会像这样运行:

    var
      hFile: THandle;
    ....
    hFile := CreateFile(
      PChar(LogFileName),
      GENERIC_WRITE,
      FILE_SHARE_READ,
      nil,
      OPEN_ALWAYS,
      FILE_ATTRIBUTE_NORMAL,
      0
    );
    Win32Check(hFile<>INVALID_HANDLE_VALUE);
    Try
      Stream := THandleStream.Create(hFile);
      Try
        .... code to write to the stream goes here
      Finally
        Stream.Free;
      End;
    Finally
      CloseHandle(hFile);
    End;
    

    最后,我不得不在这个答案中对你的环境做出很多猜测。将来,请始终包含您使用的 Delphi 版本。并始终包含明确的目标声明。

    【讨论】:

    • 您的回答有助于通过一个很好的解释来修复当前的实现,所以我为此 +1。但是,我不知何故认为 OP 应该退后一步,采取另一种方法,坚持使用框架中提供的更高级别的包装器。它们是跨平台的,并且很可能在未来仍然有效。
    • @WoutervanNifterick Asker 有一个 pre-Unicode Delphi,它没有高级包装器。因此我的回答。
    • @David 可能是他有 D2009+,在这种情况下有两个错误:1) 在第一个 write() 他输出 Length() 字节而不是 2 * Length()。输出是'Boss is dea'。 2) 在第二次写入中,他输出i地址,显示为chinalese 并附加到先前的输出中,他认为是时间。
    • @TomBrunberg 是的,我想这是有道理的。
    • 如果正在使用 D2009+,您可以使用 ByteLength(s) 代替 SizeOf(WideChar)*Length(s)。或者,直接使用TStreamWriter 而不是TFileStream.WriteBuffer()。这有一个额外的好处,您可以指定所需的文件编码。
    【解决方案2】:

    我要对您的代码进行一些更改。

    1. 您有一个应该返回布尔值的函数,但您从未设置返回值。

    2. 这不是“错误”,但变量i 最常用作数组的整数索引或循环变量。 Delphi 开发人员会讨厌使用i 作为字符串的代码。

    3. 看起来您只是在记录文本。 TFileStream 可能不是最好的类。

    我认为这是更简洁的代码:

    uses System.IoUtils;
    
    ...
    
    FileName := 'log.txt';
    LogLine  := 'Boss is dead '+TimeToStr(Now);
    
    TFile.AppendAllText(FileName, LogLine);
    

    http://docwiki.embarcadero.com/Libraries/XE7/en/System.IOUtils.TFile.AppendAllText

    【讨论】:

    • 如果 ASCII 字符显示为中文,给定该代码,那么可以安全地断定提问者具有 pre-Unicode Delphi
    • @DavidHeffernan:你可能是对的......可怜的提问者 :) IoUtils 是在 Delphi 中引入的...... 2010?
    【解决方案3】:

    这个怎么样:

    uses SysUtils;
    
    var s: AnsiString;
    
    s := FormatDateTime('yyyy-mm-dd hh:nn:ss', now);
    fs.Write(s[1], Length(s));
    

    【讨论】:

    • 你没有解决文本编码的问题
    • 我想我做到了,尽管没有明确表示。请注意: 1. 使用 AnsiString 确保使用纯 8 位字符串 2. 日期格式化字符串产生纯数字,没有任何可能需要特殊字符的月份或日期名称,具体取决于语言。 3. 结果字符串将被流正确写入。即使“log.txt”文件是utf-8,结果仍然是正确的,因为字符串只是数字和破折号。
    • 用户报告该文件似乎包含中文文本。
    • 用户报告时间以中文显示。我建议一种将时间写入流的替代方法,我认为这应该可以解决该问题。
    • 它显示为中文是因为写入的文本是 ANSI,但读取时文件被解释为 UTF-16 编码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-15
    • 2018-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多