"Text" oder "Nicht-Text", das ist hier die Frage

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Aliobaba
Lazarusforum e. V.
Beiträge: 496
Registriert: Di 1. Mai 2012, 09:11

"Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von Aliobaba »

Hallo,

Ich möchte gerne alle Dateien eines Verzeichnisses/Ordners in Memo-Felder der Reihe nach einlesen. Dies geht natürlich nur problemlos, wenn es sich bei diesem File um ein reines Text-File handelt. Falls es kein Text-File ist schreibe ich nur den Link zu dieser Datei in das Memo Feld.

Wie kann ich nun nach dem Einlesen eines Files differenzieren, ob das File ein reines Text-File ist oder nicht (also z.B. ein Bild oder ein PDF-File).

Ich bestimme die reine Filegröße und auch die Größe des Files, wenn die Sonderzeichen entfernt sind. Ist hier die Differenz groß, so ist das ein Hinweis darauf, dass es sich um KEIN Textfile handelt.
Dies funktioniert eigentlich recht gut, aber es scheint mir nicht gerade die eleganteste Methode zu sein.

Wie könnte man dies besser machen? [An Hand des File-Namen (z.B. ".txt") geht's nicht, da das Programm auch unter Linux läuft.]

Ich habe mir also so beholfen, dass ich nachsehe, wie groß der Unterschied in der Größe

Code: Alles auswählen

 
   File_Eingabe := Form1.OpenDialog1.FileName;
   SL_Text.LoadFromFile(SysToUTF8( ADateiPfad ))// einlesen
 
   Filegroesse_File := Globl.Filegroesse_ausgeben( ADateiPfad )// Filegroesse_ausgeben ist eine Funktion
   Filegroesse_Text := Length( Globl.GetOnlyChars( SL_Text.Text ) );
 
   if  (( Filegroesse_File div Filegroesse_Text )  > 1 ) then   // sehr viele Sonderzeichen als Hinweis für "kein Text"
  begin
  ....
  end;

Danke!
Aliobaba
"MyMemoryDB" ( https://www.heise.de/download/product/mymemorydb-89626 )

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von wp_xyz »

Reine Text-Dateien enthalten in der Regel keine Zeichen unter #32, außer #9 (Tabulator) und #10, #13 (Zeilenumbruch).

Ansonsten würde ich als erstes den Dateianfang auf die Header der üblichen Dateiformate prüfen - das geht viel schneller, als die ganze Datei zu durchsuchen:
  • bmp: die ersten zwei Bytes sind 'BM'
  • jpg: beginnen mit FF D8 FF E0 00 10 4A 46 49 46 00 oder falls Exif vorhanden ist auch mit FF D8 FF E1 HL LL 45 78 69 66 00 00 49 49 2A 00 08 00 00 00 oder FF D8 FF E1 HL LL 45 78 69 66 00 00 4D 4D 00 2A 00 00 00 08
  • pdf: beginnen mit '%PDF-1.7' (wahrscheinlich auch mit etwas anderem als '1.7')
  • usw. -- google is your friend...

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von theo »

Ich würde das ähnlich machen wie wp_xyz.

Erst einmal auf die zu erwartenden Dateiformate prüfen (Magic numbers) um die Geschwindigkeit zu optimieren. https://en.wikipedia.org/wiki/List_of_file_signatures

Falls unbekannt, die Datei mit genannten Ausnahmen auf Bytes <32 testen. 0 nicht vergessen.

Falls es überall "durchfällt" ist es wahrscheinlich Text.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von Mathias »

[An Hand des File-Namen (z.B. ".txt") geht's nicht, da das Programm auch unter Linux läuft.]

Unter Windows ist es klar, dort kann man es anhand der Extension erkennen. Wie du schon sagt, gibt es unter Linux viele Dateien, welche gar kein Extension haben.
Aber dafür gibt es unter Linux der MIME-Typ, der sagt, ob es eine Text-Datei ist. Nur müsste ich da auch nachschauen, wie dies geht.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 331
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon (Windows wenn notwendig), Lazarus 3.0 FPC 3.3.1

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von Niesi »

Mathias hat geschrieben:
[An Hand des File-Namen (z.B. ".txt") geht's nicht, da das Programm auch unter Linux läuft.]

Unter Windows ist es klar, dort kann man es anhand der Extension erkennen. Wie du schon sagt, gibt es unter Linux viele Dateien, welche gar kein Extension haben.
Aber dafür gibt es unter Linux der MIME-Typ, der sagt, ob es eine Text-Datei ist. Nur müsste ich da auch nachschauen, wie dies geht.



Wie kommst Du denn darauf?
Hast Du noch nie mit *.ste, *.sta, *.dze, *.dza, *.rie, *.ria, *.bat, *.cfg, *.ini usw. zu tun gehabt?
Das sind alles reine Textdateien, leider in den unterschiedlichsten Codierungen (Utf8, Utf16, ANSI u. a.), je nach Herkunftsland und Rechner.
Mit dem Betriebssystem hat das nichts zu tun.
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

Aliobaba
Lazarusforum e. V.
Beiträge: 496
Registriert: Di 1. Mai 2012, 09:11

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von Aliobaba »

:) :)
Danke schon mal für die Antworten.
Aber wenn ich das hier so lese, dann bin ich mit meinem 3-zeiligen Code schon recht zufrieden - hat bisher ja auch immer funktioniert, wobei allerdings "Lücken" eben gut denkbar sind.
Ich habe mir mittlerweile auch diverse Files mit "Kate" (Linux) angesehen: Überall -, auch in Libre-Office Textfiles - sind jede Menge kryptische Sonderzeichen, so dass "meine" Methode offenbar so schlecht nicht ist.
Eure Anregungen können mir aber nun helfen, dass ich bei dieser Methode wenigstens noch speziell Word- bzw. LibreOffice/OpenOffice-Textdateien als "Text" identifiziere, den reinen Text "rausziehe", um diesen "reinen" Text dann in einem Memo zu speichern.

Hat jemand Tips dazu?

Schönes Wochenende!
Aliobaba
"MyMemoryDB" ( https://www.heise.de/download/product/mymemorydb-89626 )

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von theo »

Aliobaba hat geschrieben:Eure Anregungen können mir aber nun helfen, dass ich bei dieser Methode wenigstens noch speziell Word- bzw. LibreOffice/OpenOffice-Textdateien als "Text" identifiziere, den reinen Text "rausziehe", um diesen "reinen" Text dann in einem Memo zu speichern.


Ist nicht ganz einfach. Für OpenOffice ODT könnte man es noch in Pascal hinkriegen.
Ansonsten kommt es auch auf die Plattform und die Gegebenheiten an.
Allgemein, bzw. für Linux vielleicht mit dem Aufruf externer Tools:
http://davidmburke.com/2014/02/04/pytho ... breoffice/

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von wp_xyz »

Aliobaba hat geschrieben::) :)
Aber wenn ich das hier so lese, dann bin ich mit meinem 3-zeiligen Code schon recht zufrieden - hat bisher ja auch immer funktioniert, wobei allerdings "Lücken" eben gut denkbar sind.

Das ist ein Trugschluss, kürzerer Code ist nicht immer besser. Versuche mal mit deinem Ansatz, ein paar richtig fette Videodateien zu analysieren. Und dann dagegen theos und mein Vorschlag, sich zuerst die paar Byte Header anzuschauen und anhand bekannter Signaturen bekannte binäre Dateitypen auszuschließen.

LibreOffice-Dateien sind gezippte Verzeichnisse mehrerer xml-Dateien. Gezippt heißt: binär - wird in deinem Verfahren also schon gar nicht als Text-Datei erkannt. Und um den Text zu extrahieren, musst du den xml-Text der richtigen Datei in dem Ordner durchgehen und jeden xml-Node analysieren. Das schaffst du sicher auch mit 3 Zeilen.

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von theo »

theo hat geschrieben:
Aliobaba hat geschrieben:Eure Anregungen können mir aber nun helfen, dass ich bei dieser Methode wenigstens noch speziell Word- bzw. LibreOffice/OpenOffice-Textdateien als "Text" identifiziere, den reinen Text "rausziehe", um diesen "reinen" Text dann in einem Memo zu speichern.

Ist nicht ganz einfach. Für OpenOffice ODT könnte man es noch in Pascal hinkriegen.


Habe dir hier mal quick and dirty zusammengehackt, wie man mit Bordmitteln den Text eines ODT lesen kann.
Vielleicht reicht dir das schon fast.

Code: Alles auswählen

unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ExtCtrls, XMLRead, DOM, xmlutils, zipper;
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    OpenDialog1: TOpenDialog;
    Panel1: TPanel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure DoCreateOutZipStream(Sender: TObject; var AStream: TStream;
      AItem: TFullZipFileEntry);
    procedure DoDoneOutZipStream(Sender: TObject; var AStream: TStream;
      AItem: TFullZipFileEntry);
    procedure ExtractFileFromZip(ZipName, FileName: string);
  private
 
  public
 
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
    ExtractFileFromZip(OpenDialog1.FileName, 'content.xml');
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  OpenDialog1.Filter := 'OpenOffice (*.odt)|*.odt';
  Memo1.ScrollBars := ssAutoBoth;
end;
 
procedure TForm1.DoCreateOutZipStream(Sender: TObject; var AStream: TStream;
  AItem: TFullZipFileEntry);
begin
  AStream := TMemorystream.Create;
end;
 
procedure TForm1.DoDoneOutZipStream(Sender: TObject; var AStream: TStream;
  AItem: TFullZipFileEntry);
var
  XMLContent: TXMLDocument;
  nd: TDomNode;
  OutText: XMLString;
 
  procedure _ReadItem(nd: TDomNode);
  begin
    if nd.NodeName = '#text' then
    begin
      Outtext := Outtext + nd.NodeValue;
    end;
    if nd.NodeName = 'text:tab' then
      Outtext := Outtext + #9;
    if nd.NodeName = 'text:line-break' then
      Outtext := Outtext + LineEnding;
  end;
 
  procedure _ReadContainer(nd: TDomNode);
  var
    i: integer;
  begin
    if pos('text:', nd.NodeName) > 0 then
    begin
      if (nd.NodeName = 'text:p') or (nd.NodeName = 'text:h') then
      begin
        Outtext := Outtext + LineEnding;
      end;
    end;
 
    if nd.HasChildNodes then
    begin
      for i := 0 to (nd.ChildNodes.Count - 1) do
        _ReadContainer(nd.ChildNodes[i]);
    end
    else
    begin
      _ReadItem(nd);
    end;
  end;
 
begin
  Outtext := '';
  AStream.Position := 0;
  XMLContent := TXMLDocument.Create;
  ReadXMLFile(XMLContent, AStream);
  Astream.Free;
  nd := XMLContent.DocumentElement.FindNode('office:body');
  if nd <> nil then
    nd := nd.Findnode('office:text');
  if nd <> nil then
    _ReadContainer(nd);
  XMLContent.Free;
  Memo1.Text := Trim(UTF8Encode(OutText));
end;
 
procedure TForm1.ExtractFileFromZip(ZipName, FileName: string);
var
  ZipFile: TUnZipper;
  sl: TStringList;
begin
  sl := TStringList.Create;
  sl.Add(FileName);
  ZipFile := TUnZipper.Create;
  try
    ZipFile.FileName := ZipName;
    ZipFile.OnCreateStream := @DoCreateOutZipStream;
    ZipFile.OnDoneStream := @DoDoneOutZipStream;
    ZipFile.UnZipFiles(sl);
  finally
    ZipFile.Free;
    sl.Free;
  end;
end;
 
end.

Aliobaba
Lazarusforum e. V.
Beiträge: 496
Registriert: Di 1. Mai 2012, 09:11

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von Aliobaba »

:shock:
Wowh!
... und so etwas geht 'mal ebenso' "quick and dirty".
Ich glaube, ich höre auf mit meinen Programmierversuchen! :oops:

Danke Theo!!
"MyMemoryDB" ( https://www.heise.de/download/product/mymemorydb-89626 )

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von theo »

Aliobaba hat geschrieben::shock:
Wowh!
... und so etwas geht 'mal ebenso' "quick and dirty".
Ich glaube, ich höre auf mit meinen Programmierversuchen! :oops:


Nee, ich hatte das mal im grösseren Kontext gemacht, mit Schriftstilen und dem ganzen Brimborium.
Daraus habe ich dir "quick and dirty" das Wesentliche extrahiert, um nur den reinen Text darzustellen.

Zu deiner ursprünglichen Frage: Unter Unixen gibt es das Kommando File, welche genau diese Info liefert.
Der Aufruf dieses Kommandos aus Lazarus wäre u.U. sogar schneller, als das Einlesen der ganzen Dateien.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von Mathias »

Zu deiner ursprünglichen Frage: Unter Unixen gibt es das Kommando File, welche genau diese Info liefert.
Der Aufruf dieses Kommandos aus Lazarus wäre u.U. sogar schneller, als das Einlesen der Datei.

Das ist genau die MIME-Typ, welches ich oben beschrieben habe. Das Kommando "file" kannte ich noch nicht.

Die Frage, kann man den MIME-Typ auch direkt von Lazarus aus abfragen ?
ZB. mit der Unit BaseUnix.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: "Text" oder "Nicht-Text", das ist hier die Frage

Beitrag von theo »

Mathias hat geschrieben:Die Frage, kann man den MIME-Typ auch direkt von Lazarus aus abfragen ?
ZB. mit der Unit BaseUnix.


Glaube ich nicht. Das ist schon ein spezialisiertes Programm, welches auf eine Datenbank zurückgreift.
z.B. https://specifications.freedesktop.org/ ... atest.html

Antworten