Seite 1 von 2

ReadTextFile

Verfasst: 30.08.2008 13:39
von milan1612
Da relativ oft gefragt wird wie man eine Textdatei richtig ausliest, hab ich
mal schnell eine Funktion geschrieben. Ich hoffe ich hab alles beachtet, wenn
nicht bitte sagen.

Code: Alles auswählen

Procedure.s ReadTextFile(Path.s)
  Protected File = ReadFile(#PB_Any, Path)
  Protected Content.s
  If File
    Protected BOM = ReadStringFormat(File)
    If BOM = #PB_Ascii Or BOM = #PB_Unicode Or BOM = #PB_UTF8
      Protected Size.l = Lof(File) - Loc(File) + SizeOf(Character)
      Protected *Buffer = AllocateMemory(Size)
      If *Buffer
        Protected Len = ReadData(File, *Buffer, Size - SizeOf(Character))
        If Len
          PokeC(*Buffer + Size - SizeOf(Character), 0)
          Content = PeekS(*Buffer, -1, BOM)
          FreeMemory(*Buffer)
        EndIf
      EndIf
    EndIf
    CloseFile(File)
  EndIf
  ProcedureReturn Content
EndProcedure

MessageRequester("", ReadTextFile("C:\Test.txt"))
EDIT: Code ausgebessert - Danke an AND51
EDIT2: PeekS Length Parameter auf -1 gesetzt
EDIT3: Erzwungene Null Terminierung geadded

Verfasst: 30.08.2008 19:28
von Andesdaf
Schön, jetz brauch ich das nicht immer tausendmal wieder schreiben...

Verfasst: 30.08.2008 20:40
von AND51
@ milan1612:
Dein Parameter "Path" ist falsch, er sollte "File$" oder so heißen. Aber wichtiger ist, dass du das BOM nicht ganz korrekt handhabst.

Wenn ein BOM gefunden wird, wird der Lesezeiger automatisch an das Ende des BOMs gesetzt, also werden die ersten 4 Byte übersprungen. Folglich liest du immer 4 Byte zu viel ein und reservierst einen zu großen Buffer, wenn ein BOM vorhanden ist. Auch bei deinem PeekS() kommt dann die falsche Längenangabe zum Einsatz.

Code: Alles auswählen

Procedure.s ReadTextFile(File$)
	Protected text.s, id=ReadFile(#PB_Any, File$)
	If id
		Protected bom=ReadStringFormat(id)
		If Not bom&4
			Protected size=Lof(id)-Loc(id), *buffer=AllocateMemory(size)
			If *buffer
				ReadData(id, *buffer, size)
				CloseFile(id)
				text=PeekS(*buffer, size, bom)
				FreeMemory(*buffer)
			EndIf
		EndIf
	EndIf
	ProcedureReturn text
EndProcedure

Verfasst: 30.08.2008 21:26
von milan1612
@AND
Danke, das mit dem BOM wusste ich nicht. Allerdings sollte man bei PeekS
lieber den Rückgabewert von ReadData verwenden. Der sagt dir nämlich wie
viel denn jetzt wirklich gelesen wurden - bei I/O-Operationen muss man da aufpassen.

Verfasst: 30.08.2008 21:35
von AND51
Im Grunde hast du Recht.
Ich will deines jetzt auch nicht schlecht reden, aber mir fällt spontan kein Fall ein, bei dem meine Prozedur versagen würde.
Wenn Lof() sagt, die Datei ist 10 Byte groß, wieso (unter welchen Umständen) sollte ReadData() dann weniger lesen?

Das würde mich brennend interessieren.

Verfasst: 30.08.2008 21:44
von milan1612
AND51 hat geschrieben:Im Grunde hast du Recht.
Ich will deines jetzt auch nicht schlecht reden, aber mir fällt spontan kein Fall ein, bei dem meine Prozedur versagen würde.
Wenn Lof() sagt, die Datei ist 10 Byte groß, wieso (unter welchen Umständen) sollte ReadData() dann weniger lesen?

Das würde mich brennend interessieren.
Weiß jetzt auch nicht, aber ich dachte immer dass ja theoretisch beim Lesen
ein I/O Hardware-Fehler passieren könnte - keine Ahnung ob man das jetzt berücksichtigen sollte.
Is ja auch egal, deine Procedure funktioniert ja perfekt.
Und danke nochmal für die BOM-Berichtigung - man lernt nie aus...

Verfasst: 30.08.2008 22:07
von ts-soft
Da ganze Textfiles oftmals im Zusammenhang mit dem EditorGadget
benötigt werden, hier mal ein Beispiel für Streaming (PlainText oder RichText):

Code: Alles auswählen

Procedure StreamFileIn_Callback(hFile, pbBuff, cb, pcb)
  ProcedureReturn ReadFile_(hFile, pbBuff, cb, pcb, 0)!1
EndProcedure

Procedure Editor_Load(Gadget, File.s)
  Protected FileID = ReadFile(#PB_Any, File)
  Protected StreamData.EDITSTREAM
  If FileID
    StreamData\dwCookie = FileID(FileID)
    StreamData\dwError = #Null
    StreamData\pfnCallback = @StreamFileIn_Callback()
    If UCase(GetExtensionPart(File)) = "RTF"
      SendMessage_(GadgetID(Gadget), #EM_STREAMIN, #SF_RTF, @StreamData)
    Else
      SendMessage_(GadgetID(Gadget), #EM_STREAMIN, #SF_TEXT, @StreamData)
    EndIf
    CloseFile(FileID)
  EndIf
EndProcedure

If OpenWindow(0, #PB_Ignore, #PB_Ignore, 640, 480, "Editor-Test", #PB_Window_SystemMenu | #PB_Window_Invisible)
  If CreateGadgetList(WindowID(0))
    EditorGadget(0, 5, 5, WindowWidth(0) - 10, WindowHeight(0) - 10)
    Editor_Load(0, #PB_Compiler_Home + "Library SDK\Readme.txt")
    
    While WindowEvent() : Wend
    HideWindow(0, #False)
    
    While WaitWindowEvent() <> #PB_Event_CloseWindow : Wend
  EndIf
EndIf
Das ist die schnellste Methode :wink:

Gruß
Thomas

Verfasst: 30.08.2008 23:51
von AND51
milan1612 hat geschrieben:Is ja auch egal, deine Procedure funktioniert ja perfekt.
Und danke nochmal für die BOM-Berichtigung - man lernt nie aus...
Den genauen Wortlaut findest du in der Help-File unter ReadStringFormat().

Ansonsten: :allright:

Verfasst: 26.06.2009 11:14
von Kukulkan
Hi,

Ich war auf der Suche nach Unicode-Laden und hab die Routine von milan1612 mal getestet. Leider scheint das auch nicht korrekt zu funktionieren.

Die Länge meiner UTF-16 (Unicode) Datei ist 1400 Zeichen incl. BOM (2 Byte). Die Routine liest eine korrekte Size von 1398 Bytes und nimmt das in den *buffer. Jetzt wird an PeekS() diese Size übergeben. Ich bekomme nun einen String mit 1398 Bytes(!) zurück. Die erste Hälfte ist der korrekte Text und die zweite Hälfte ist Schrott, weil der Unicode-String (2 Byte je Zeichen) in einen ASCII String gewandelt wurde (1 Byte je Zeichen). Ich hab ja keine Unicode-EXE.

Bei Unicode kann ich das problemlos wegschneiden:

Code: Alles auswählen

If BOM.l = #PB_Unicode: Len.l = Len.l / 2: EndIf ; half the size for unicode!
Aber was mache ich bei UTF-8?

Volker

Verfasst: 26.06.2009 13:02
von Little John
Volker Schmid hat geschrieben:Die Länge meiner UTF-16 (Unicode) Datei ist 1400 Zeichen incl. BOM (2 Byte).
Das sind dann also 2800 Bytes.
Volker Schmid hat geschrieben:Die Routine liest eine korrekte Size von 1398 Bytes
1398 Bytes ist nicht die korrekte Größe von 1400 UTF-16-Zeichen.
Volker Schmid hat geschrieben:Bei Unicode kann ich das problemlos wegschneiden:

Code: Alles auswählen

If BOM.l = #PB_Unicode: Len.l = Len.l / 2: EndIf ; half the size for unicode!
Aber was mache ich bei UTF-8?
Ich bin mir nicht sicher, ob ich die Frage richtig verstanden habe, aber kann es sein dass die Flags von PeekS()
#PB_Ascii : Liest den String als ASCII, auch wenn das Programm im Unicode-Modus kompiliert wurde
#PB_UTF8 : Liest den String als UTF8 (das Programm kann sowohl im Unicode- als auch im ASCII-Modus sein)
#PB_Unicode: Liest den String als Unicode, auch wenn das Programm im ASCII-Modus kompiliert wurde
das sind, was Du suchst?

Gruß, Little John