ReadTextFile

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
milan1612
Beiträge: 810
Registriert: 15.04.2007 17:58

ReadTextFile

Beitrag 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
Zuletzt geändert von milan1612 am 26.06.2009 13:18, insgesamt 4-mal geändert.
Bin nur noch sehr selten hier, bitte nur noch per PN kontaktieren
Andesdaf
Moderator
Beiträge: 2673
Registriert: 15.06.2008 18:22
Wohnort: Dresden

Beitrag von Andesdaf »

Schön, jetz brauch ich das nicht immer tausendmal wieder schreiben...
Win11 x64 | PB 6.20
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag 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
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
milan1612
Beiträge: 810
Registriert: 15.04.2007 17:58

Beitrag 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.
Bin nur noch sehr selten hier, bitte nur noch per PN kontaktieren
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag 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.
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
milan1612
Beiträge: 810
Registriert: 15.04.2007 17:58

Beitrag 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...
Bin nur noch sehr selten hier, bitte nur noch per PN kontaktieren
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Beitrag 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
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag 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:
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
Kukulkan
Beiträge: 1066
Registriert: 09.09.2004 07:07
Wohnort: Süddeutschland
Kontaktdaten:

Beitrag 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
Little John

Beitrag 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
Antworten