Einzelnen Beitrag anzeigen

PeterPanino

Registriert seit: 4. Sep 2004
1.451 Beiträge
 
Delphi 10.4 Sydney
 
#30

AW: Datei auf Indikatoren für Binärdatei testen?

  Alt 10. Mai 2015, 13:09
Ich habe jetzt den Hinweis von @mjustin (Posting #8) berücksichtigt, dass UTF-32-enkodierte Dateien auch Doppel-NullBytes enthalten können und deshalb eine Prüfung auf UTF-32 BOM eingefügt (BE und LE), wenn ein Doppel-NullByte angetroffen wird, da ich optimistischerweise davon ausgehe, dass UTF-32-enkodierte Dateien einen BOM enthalten. Damit wird die Rest-Unsicherheit für den Ausschluss von Binärdateien verkleinert. Eine weitere Verkleinerung dieser Rest-Unsicherheit bei einem Doppel-NullByte-Vorkommen in einer Datei ohne UTF-32 BOM könnte man nur durch eine aufwendige statistische oder linguistische Analyse der in der Datei enthaltenen Bytes erreichen.

Delphi-Quellcode:
function IsTextFile(const AFile: string; const ABytesCount: Integer = 1000): Boolean;
// testet, ob die ersten ABytesCount Bytes einer Datei Indikatoren für eine Binär-Datei enthalten:
// wenn nicht, muss es wohl eine Textdatei sein?
// Siehe auch: http://qc.embarcadero.com/wc/qcmain.aspx?d=84071
const
  MaxAllowedForbiddenControlCharsCount = 1;
  BOM_UTF32_LSB: array [0..3] of Byte = ($FF,$FE,$00,$00);
  BOM_UTF32_MSB: array [0..3] of Byte = ($00,$00,$FE,$FF);
var
  Reader: TStreamReader;
  Ch: AnsiChar;
  c: Integer;
  PreviousCharWasNullByte: Boolean;
  ForbiddenControlCharsCount: Integer;

  function HasUTF32BOM(S: TStream): Boolean;
  var
    SavedPos: Int64;
    Buf: TBytes;
  begin
    SetLength(Buf, 4);
    SavedPos := S.Position;
    Result := False;
    try
      S.Seek(0, soBeginning);
      if S.Read(Buf, 4) = 4 then
      begin
        Result := ((Buf[0] = BOM_UTF32_LSB[0])
               and (Buf[1] = BOM_UTF32_LSB[1])
               and (Buf[2] = BOM_UTF32_LSB[2])
               and (Buf[3] = BOM_UTF32_LSB[3]))
               or
                  ((Buf[0] = BOM_UTF32_MSB[0])
               and (Buf[1] = BOM_UTF32_MSB[1])
               and (Buf[2] = BOM_UTF32_MSB[2])
               and (Buf[3] = BOM_UTF32_MSB[3]));
      end;
      CodeSite.Send('HasUTF32BOM', Result);
    finally
      S.Position := SavedPos;
    end;
  end;
begin
  Result := True;
  c := 0;
  PreviousCharWasNullByte := False;
  ForbiddenControlCharsCount := 0;

  Reader := TStreamReader.Create(TFileStream.Create(AFile, fmOpenRead), TEncoding.ANSI);
  try
    if Reader.EndOfStream then
    begin
      CodeSite.Send('Nothing to read');
      Result := False;
      EXIT;
    end;

    while Reader.Peek() >= 0 do
    begin
      Ch := AnsiChar(Reader.Read());

      if Ch = #0 then
      begin
        if PreviousCharWasNullByte then
        begin
          CodeSite.Send('Double Null Byte found');
          Result := HasUTF32BOM(Reader.BaseStream); // False;
          EXIT;
        end;
        PreviousCharWasNullByte := True;
      end
      else
      begin
        PreviousCharWasNullByte := False;

        if Ch in [#1..#8, #14..#31] then
        begin
          Inc(ForbiddenControlCharsCount);
          CodeSite.Send('This forbidden control char', HexDisplayPrefix + IntToHex(Ord(Ch), 2));
        end;
        if ForbiddenControlCharsCount > MaxAllowedForbiddenControlCharsCount then
        begin
          CodeSite.Send('More than ' + IntToStr(MaxAllowedForbiddenControlCharsCount) + ' forbidden control chars found');
          Result := False;
          EXIT;
        end;
      end;

      // Todo: andere Indikatoren?

      Inc(c);
      if c > ABytesCount then EXIT;
    end;
  finally
    CodeSite.Send('Bytes read', c);
    Reader.Close();
    Reader.BaseStream.Free;
    Reader.Free();
  end;
end;

procedure TForm1.btnTestClick(Sender: TObject);
var
  d: Int64;
begin
  d := GetTickCount;
  CodeSite.Send('Is Text File?', IsTextFile(edt1.Text));
  CodeSite.Send('Duration', GetTickCount - d);
end;
So bleibt als nächster Schritt nur noch die zusätzliche Prüfung auf UTF-16 BOM beim Antreffen der Mindestzahl von verbotenen ControlBytes.

Geändert von PeterPanino (10. Mai 2015 um 13:23 Uhr)
  Mit Zitat antworten Zitat