Seite 1 von 1

Datei Blockweise lesen und schreiben

Verfasst: 10.11.2011 06:30
von Silver
Hallo zusammen,

Wenn ich folgendes Beispielprogramm ausführe stoppt der PB Compiler mit der Fehlermeldung :
(error) der angegebene Buffer ist 0.
Kann mir jemand freundlicherweise erklären wo der Fehler liegt ?
Danke und Gruß,

Silver

Code: Alles auswählen

; German forum: http://www.purebasic.fr/german/archive/viewtopic.php?t=2374&highlight=
; Author: NicTheQuick (updated for PB3.92+ by Lars, updated for PB4.00 by blbltheworm)
; Date: 24. September 2003
; OS: Windows
; Demo: Yes

; Read (then you can do probably some changes) and write files blockwise
; (example here works with 4096 Byte memory blocks

; Die Procedure liest die erste Datei ein und entfernt aus ihr alle Zeichen mit dem
; ASCII-Code 32, 13 und 10. Bei meiner 10 MB Primzahlen-Datei dauert das ganze ca.
; 2 Sekunden mit aktiviertem Debugger.

#Block = 4096 
Procedure CopyFiles(File1.s, File2.s) 
  Protected *Mem0.BYTE, *Mem1.BYTE 
  Protected MaxBytes.l, ChangedBytes.l 
  
  If ReadFile(0, File1)   ;1. Datei zum Lesen öffnen 
    If CreateFile(1, File2)   ;2. Datei zum Schreiben öffnen 
      Mem0 = AllocateMemory(#Block)  ;Speicher 0 mit 4096 Bytes reservieren 
      If Mem0
        Mem1 = AllocateMemory(#Block)  ;Speicher 1  mit 4096 Bytes reservieren 
        If Mem1
          While Eof(0) = 0  ;Wiederhole diese Schleife bis die Datei zu Ende ist 
            ;Benutze Lesedatei 
            If Lof(0) - Loc(0) > #Block   ;Wenn die restlichen Bytes in der Datei mehr als 4096 
              MaxBytes = #Block         ;Bytes betragen, dann begrenze das auf 4096 Bytes, 
            Else                        ;ansonsten nimm den Rest. 
              MaxBytes = Lof(0) - Loc(0) 
            EndIf 
            
            ReadData(0,*Mem0, MaxBytes)   ;Lies den nächsten Datenblock in der Datei in Speicher 0 ein 
            
            ;Quelle ändern 
            ChangedBytes = 0 
            For a = 1 To MaxBytes   ;Wiederhole die Schleife so oft wie Bytes vorhanden sind 
              If *Mem0\b <> 32 And *Mem0\b <> 13 And *Mem0\b <> 10  ;Wenn kein Zeichen mit 32, 13, 10 da ist 
                *Mem1\b = *Mem0\b   ;Speichere das Byte von Speicher 0 in Speicher 1 
                *Mem1 + 1           ;Gehe in Speicher 1 um ein Byte weiter 
                ChangedBytes + 1    ;Zähle die in Speicher 1 abgelegten Bytes 
              EndIf 
              *Mem0 + 1     ;Gehe in Speicher 0 um ein Byte weiter 
            Next 
            ;Benutze die Schreibdatei 
            WriteData(1,Mem1, ChangedBytes)   ;Schreibe die in Speicher 1 abgelegten Bytes in die Datei 
          Wend 
          FreeMemory(Mem1)   ;Gib Speicher 1 wieder frei 
        EndIf 
        FreeMemory(Mem0)   ;Gib Speicher 0 wieder frei 
      EndIf 
      CloseFile(1)    ;Schließe Datei 1 
    EndIf 
    CloseFile(0)    ;Schließe Datei 0 
  EndIf 
EndProcedure 

CopyFiles("c:\Primzahlen1.txt", "c:\Primzahlen1.txt2")

; IDE Options = PureBasic v4.00 (Windows - x86)
; Folding = -

Re: Datei Blockweise lesen und schreiben

Verfasst: 10.11.2011 07:59
von Danilo
Silver hat geschrieben:Hallo zusammen,

Wenn ich folgendes Beispielprogramm ausführe stoppt der PB Compiler mit der Fehlermeldung :
(error) der angegebene Buffer ist 0.
Kann mir jemand freundlicherweise erklären wo der Fehler liegt ?
Am Anfang der Procedure, bei AllocateMemory, nimmst Du die Variablen Mem0 und Mem1.
Danach nimmst Du *Mem0 und *Mem1 - was verschiedene Variablen sind.

Mach mal ein EnableExplicit vor die Procedure, dann siehst Du die Fehler
und kannst Mem0 & Mem1 überall zu *Mem0 und *Mem1 ändern.

Aber noch etwas anderes zu diesem Ausschnitt:

Code: Alles auswählen

            ;Quelle ändern
            ChangedBytes = 0
            For a = 1 To MaxBytes   ;Wiederhole die Schleife so oft wie Bytes vorhanden sind
              If *Mem0\b <> 32 And *Mem0\b <> 13 And *Mem0\b <> 10  ;Wenn kein Zeichen mit 32, 13, 10 da ist
                *Mem1\b = *Mem0\b   ;Speichere das Byte von Speicher 0 in Speicher 1
                *Mem1 + 1           ;Gehe in Speicher 1 um ein Byte weiter
                ChangedBytes + 1    ;Zähle die in Speicher 1 abgelegten Bytes
              EndIf
              *Mem0 + 1     ;Gehe in Speicher 0 um ein Byte weiter
            Next
Das sieht mir etwas komisch aus. Hier werden alle Zeichen außer ASCII 32, 13, 10
kopiert. Für die nicht gewünschten Zeichen wird aber kein Wert gesetzt, so daß
dort der Bufferwert des vorherigen Block-Durchlaufs verbleibt. Somit also ein zufälliges Zeichen!

Wofür braucht man denn sowas? Eine Funktion die in ASCII-Dateien die Zeichen 32/13/10
durch zufällige Werte ersetzt? Im ersten Durchlauf steht noch 0 drin, aber in folgenden
Durchläufen stehen Werte des vorherigen Blocks noch in den Buffern.

Re: Datei Blockweise lesen und schreiben

Verfasst: 10.11.2011 08:04
von NicTheQuick
Ich wusste gar nicht, dass ich sowas mal geschrieben hatte. Na hier jedenfalls der funktionierende Code.

Code: Alles auswählen

; German forum: http://www.purebasic.fr/german/archive/viewtopic.php?t=2374&highlight=
; Author: NicTheQuick (updated for PB3.92+ by Lars, updated for PB4.00 by blbltheworm)
; Date: 24. September 2003
; OS: Windows
; Demo: Yes

; Read (then you can do probably some changes) and write files blockwise
; (example here works with 4096 Byte memory blocks

; Die Procedure liest die erste Datei ein und entfernt aus ihr alle Zeichen mit dem
; ASCII-Code 32, 13 und 10. Bei meiner 10 MB Primzahlen-Datei dauert das ganze ca.
; 2 Sekunden mit aktiviertem Debugger.

EnableExplicit

#Block = 4096
Procedure CopyFiles(file1.s, file2.s)
	Protected *mem0.Byte, *mem1.BYte
	Protected *c0.Byte, *c1.Byte
	Protected maxBytes.i, changedBytes.i
	Protected hFile1.i, hFile2.i
	Protected a.i
	
	hFile1 = ReadFile(#PB_Any, file1)   ;1. Datei zum Lesen öffnen
	If hFile1
		hFile2 = CreateFile(#PB_Any, file2)   ;2. Datei zum Schreiben öffnen
		If hFile2
			*mem0 = AllocateMemory(#Block)  ;Speicher 0 mit 4096 Bytes reservieren
			If *mem0
				*mem1 = AllocateMemory(#Block)  ;Speicher 1  mit 4096 Bytes reservieren
				If *mem1
					While Loc(hFile1) < Lof(hFile1)  ;Wiederhole diese Schleife bis die Datei zu Ende ist
						;Benutze Lesedatei
						If Lof(hFile1) - Loc(hFile1) > #Block   ;Wenn die restlichen Bytes in der Datei mehr als 4096
							maxBytes = #Block         ;Bytes betragen, dann begrenze das auf 4096 Bytes,
						Else                        ;ansonsten nimm den Rest.
							maxBytes = Lof(hFile1) - Loc(hFile1)
						EndIf
						
						ReadData(hFile1, *mem0, maxBytes)   ;Lies den nächsten Datenblock in der Datei in Speicher 0 ein
						
						;Quelle ändern
						changedBytes = 0
						*c0 = *mem0
						*c1 = *mem1
						For a = 1 To maxBytes   ;Wiederhole die Schleife so oft wie Bytes vorhanden sind
							If *c0\b <> 32 And *c0\b <> 13 And *c0\b <> 10  ;Wenn kein Zeichen mit 32, 13, 10 da ist
								*c1\b = *c0\b   ;Speichere das Byte von Speicher 0 in Speicher 1
								*c1 + 1           ;Gehe in Speicher 1 um ein Byte weiter
								changedBytes + 1    ;Zähle die in Speicher 1 abgelegten Bytes
							EndIf
							*c0 + 1     ;Gehe in Speicher 0 um ein Byte weiter
						Next
						;Benutze die Schreibdatei
						WriteData(hFile2, *mem1, changedBytes)   ;Schreibe die in Speicher 1 abgelegten Bytes in die Datei
					Wend
					FreeMemory(*mem1)   ;Gib Speicher 1 wieder frei
				EndIf
				FreeMemory(*mem0)   ;Gib Speicher 0 wieder frei
			EndIf
			CloseFile(hfile2)    ;Schließe Datei 1
		EndIf
		CloseFile(hFile1)    ;Schließe Datei 0
	EndIf
EndProcedure

CopyFiles("c:\Primzahlen1.txt", "c:\Primzahlen1.txt2")
Und hier dein eigentlicher Fehler: Schau dir mal genau an, wo du ein * vor die Pointer geschrieben hast und wo nicht. :wink: Die leichteste Art solche Fehler zu finden nennt sich "EnableExplicit".

///Edit:
Und siehe da. Da war Danilo sogar noch schneller. Aber was ich noch dazu sagen kann: Ich habe die festen IDs für die Dateien durch dynamische mit #PB_Any erzeugte Handles ersetzt. So ist die Procedure universeller einsetzbar.

///Edit 2:
Hab grad mal den Ursprungsthread aus dem Archiv herausgekramt. Das war ja vom "Mittwoch, 24.09.03 - 16:37". :D Da gab's noch 'UseMemory()'. Hach, das waren noch Zeiten. :wink:

Re: Datei Blockweise lesen und schreiben

Verfasst: 10.11.2011 08:17
von Nino
Ein Programm zum Kopieren einer Datei sieht bei mir so aus:

Code: Alles auswählen

; Dies ist nur zur Demonstration, es gibt einen PB-Befehl CopyFile().

EnableExplicit

#MaxChunk = 8192

Procedure.i Copy (infile$, outfile$)
   Protected ifn, ofn, chunk
   Protected *buffer

   ifn = ReadFile(#PB_Any, infile$)
   If ifn = 0
      ProcedureReturn #False          ; Fehler
   EndIf

   ofn = CreateFile(#PB_Any, outfile$)
   If ofn = 0
      CloseFile(ifn)
      ProcedureReturn #False          ; Fehler
   EndIf

   *buffer= AllocateMemory(#MaxChunk)
   Repeat
      chunk = ReadData(ifn, *buffer, #MaxChunk)
      ; +------------------------------------------------------+
      ; | Bei Bedarf kann man den Speicherbereich, auf den     |
      ; | *buffer zeigt, vor dem  Schreiben beliebig veränden. |
      ; +------------------------------------------------------+
      WriteData(ofn, *buffer, chunk)
   Until chunk < #MaxChunk

   FreeMemory(*buffer)
   CloseFile(ofn)
   CloseFile(ifn)

   ProcedureReturn #True              ; Erfolg
EndProcedure

Define infile$, outfile$

infile$ = "source.dat"
outfile$ = "target.dat"
If Copy(infile$, outfile$)
   Debug "OK"
Else
   Debug "Error"
EndIf
Grüße, Nino

Re: Datei Blockweise lesen und schreiben

Verfasst: 10.11.2011 08:49
von Silver
Wow :o
Habe nicht so schnell mit einer Antwort gerechnet !

Danke und Gruß,

Silver