0

私は、TFileStream を使用して UTF-8 BOM をテキスト ファイルに書き込み、その後に 1 行のダミー テキストを書き込むテスト Delphi アプリケーションを作成しました。

すべてが期待どおりに機能し、Notepad++ の 16 進ビューア プラグインを使用すると、出力テキスト ファイルに BOM が表示されます。ただし、ファイルを再度開いたときにテキスト ファイルの属性を (Delphi でプログラムによって、または Windows エクスプローラーを介して) 変更すると、BOM が削除されます。

BOM とダミー データをファイルに書き込むサンプル コード:

procedure TForm1.Button1Click(Sender: TObject);
const
  cFilename = 'myfile.txt';
var
  fs : TFileStream;
  gBOM : TBytes;
  gStr : RawByteString;
begin
  fs := TFileStream.Create(cFilename, fmCreate, fmShareDenyWrite);
  try
    gBOM := TEncoding.UTF8.GetPreamble;
    fs.WriteBuffer(PAnsiChar(gBOM)^, Length(gBOM));

    // Dummy data
    gStr := UTF8Encode('Dummy string') + AnsiChar(#13) + AnsiChar(#10);
    fs.WriteBuffer(PAnsiChar(gStr)^, Length(gStr));

    // If you read the file now the BOM will be present, however
    // the follow line appears to remove it.
    FileSetAttr(cFilename, faReadOnly);

  finally
    FreeAndNil(fs);
  end;
end;
4

1 に答える 1

4

ファイル属性を設定しても、ファイルの既存のコンテンツには影響しません。BOMが消える唯一の方法は、BOMを省略してファイルのコンテンツを新しいファイルにコピーする場合です。属性の設定はそれを行いません。

相対ファイルパスを使用していることに注意してください。そのため、マシン上にファイルの複数のコピーがあり、間違ったファイルを見ている可能性があります。代わりに、常にフルパスを使用してください。

を使用してBOMとテキストをファイルに書き込む簡単な方法は、代わりにクラスTEncodingを使用することです。TStreamWriter

ファイルを閉じた後に呼び出しFileSetAttr()て実際に有効になるようにする必要があります。また、呼び出すFileGetAttr()前に呼び出しFileSetAttr()て、既存の属性が正しく保持されていることを確認する必要があります。

代わりにこれを試してください:

procedure TForm1.Button1Click(Sender: TObject); 
const 
  cFilename = 'c:\path to\myfile.txt'; 
var 
  sw : TStreamWriter;
  Attrs: Integer; 
begin 
  sw := TStreamWriter.Create(cFilename, False, TEncoding.UTF8); 
  try 
    sw.WriteLine('Dummy string');
  finally 
    sw.Free; 
  end; 
  Attrs := FileGetAttr(cFilename);
  if Attrs <> -1 then 
    FileSetAttr(cFilename, Attrs or faReadOnly); 
end; 

または:

// GetFileInformationByHandle() is declared in Windows.pas, but SetFileInformationByHandle() is not!

type
  _FILE_INFO_BY_HANDLE_CLASS = ( 
    FileBasicInfo,
    FileStandardInfo,
    FileNameInfo,
    FileRenameInfo,
    FileDispositionInfo,
    FileAllocationInfo,
    FileEndOfFileInfo,
    FileStreamInfo,
    FileCompressionInfo,
    FileAttributeTagInfo,
    FileIdBothDirectoryInfo
);
FILE_INFO_BY_HANDLE_CLASS = _FILE_INFO_BY_HANDLE_CLASS;

_FILE_BASIC_INFO = record
  CreationTime: LARGE_INTEGER;
  LastAccessTime: LARGE_INTEGER;
  LastWriteTime: LARGE_INTEGER;
  ChangeTime: LARGE_INTEGER;
  FileAttributes: DWORD;
end;
FILE_BASIC_INFO = _FILE_BASIC_INFO;

function SetFileInformationByHandle(hFile: THandle; FileInformationClass: FILE_INFO_BY_HANDLE_CLASS; lpFileInformation: Pointer; dwBufferSize: DWORD): BOOL; stdcall; external 'kernel32' delayed;

procedure TForm1.Button1Click(Sender: TObject); 
const 
  cFilename = 'c:\path to\myfile.txt'; 
var 
  sw : TStreamWriter;
  fi: TByHandleFileInformation;
  bi: FILE_BASIC_INFO;
  Attrs: Integer;
  AttrsSet: Boolean;
begin 
  AttrsSet := False;

  sw := TStreamWriter.Create(cFilename, False, TEncoding.UTF8); 
  try 
    sw.WriteLine('Dummy string');

    if CheckWin32Version(6, 0) then
    begin
      if GetFileInformationByHandle(TFileStream(sw.BaseStream).Handle, fi) then
      begin
        bi.CreationTime.LowPart := fi.ftCreationTime.dwLowDateTime;
        bi.CreationTime.HighPart := fi.ftCreationTime.dwHighDateTime;

        bi.LastAccessTime.LowPart := fi.ftLastAccessTime.dwLowDateTime;
        bi.LastAccessTime.HighPart := fi.ftLastAccessTime.dwHighDateTime;

        bi.LastWriteTime.LowPart := fi.ftLastWriteTime.dwLowDateTime;
        bi.LastWriteTime.HighPart := fi.ftLastWriteTime.dwHighDateTime;

        bi.ChangeTime := bi.LastWriteTime;

        bi.FileAttributes := fi.dwFileAttributes or FILE_ATTRIBUTE_READONLY;
        AttrsSet := SetFileInformationByHandle(TFileStream(sw.BaseStream).Handle, FileBasicInfo, @bi, SizeOf(bi));
      end;
  finally 
    sw.Free; 
  end; 

  if not AttrsSet then
  begin
    Attrs := FileGetAttr(cFilename);
    if Attrs <> -1 then 
      FileSetAttr(cFilename, Attrs or faReadOnly); 
  end;
end; 
于 2012-10-15T20:47:43.677 に答える