2012-10-15 2 views
0

TFileStream을 사용하여 UTF-8 BOM을 텍스트 파일에 쓰고 하나의 더미 텍스트 줄을 뒤 따르는 테스트 Delphi 응용 프로그램을 사용했습니다.파일 속성을 변경할 때 UTF-8 BOM이 손실되는 이유는 무엇입니까?

모두 예상대로 작동하고 Notepad ++ 용 16 진 뷰어 플러그인을 사용하면 출력 텍스트 파일에 BOM이 표시됩니다. 그러나 파일을 다시 열 때 텍스트 파일의 특성 (델파이 또는 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; 
+0

을 그래서, 당신은 후 호출하기 전에'FileSetAttr' Windows 탐색기가 17 바이트로 파일의 크기를 보여 주었다고 말하고, 그러나있어 그 전화, 그것은 14 바이트 파일을 보여 줬어? 이전에 파일을 닫은 후에 파일의 속성 *을 변경하면 동작의 차이가 있습니까? –

+0

설명하는 동작이 발생하지 않습니다. –

+0

파일 핸들이 이미 열려 있기 때문에 Windows API 함수 인 SetFileInformationByHandle을 사용하여 특성을 설정해야합니다. 그러나 나는 그것이 정말로 중요하다고 생각하지 않는다. –

답변

4

설정 파일의 속성은 파일의 기존 내용에 영향을 미치지 않습니다. BOM이 사라질 수있는 유일한 방법은 파일의 내용이 BOM이 생략 된 새 파일로 복사되는 경우입니다. 속성 설정은 그렇게하지 않습니다.

상대 파일 경로로 작업 중이므로 컴퓨터에 파일의 복사본이 여러 개 있고 잘못된 파일을보고있을 수 있습니다. 항상 전체 경로를 사용하십시오.

TEncoding으로 파일에 BOM과 텍스트를 쓰는 더 쉬운 방법은 TStreamWriter 클래스를 사용하는 것입니다.

파일을 닫은 후에 파일을 닫은 후 호출하여 으로 전화하여 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; 
관련 문제