Newsgroup: it.comp.lang.delphi
Titolo: Comportamento strano files binari e oggetto tfilestream.

Ciao a tutti,
ho riscontrato uno strano comportamento nella scrittura di un file binario 
(e sospetto si tratti di un bug non riuscendo a comprenderne la logica per 
cui viene provocato), per cui chiedo delucidazioni.
In sostanza, ho un record del tipo:
t = array [0.1023] of char;
k = record
        s: t;
      end;
x = record
        y : k;
      end;
il contenuto di y.s viene inizializzato caratteri generati casualmente (sono 
compresi di conseguenza anche quelli di controllo (0..31 del codice ascii)).
Nel tentare la scrittura del record mediante l'oggetto tfilestream, il file 
viene si scritto correttamente, ma il suo contenuto è tutto eccetto quello 
che ho dato io in input.
Scervellandomi un po' sono arrivato a capire che ciò è dovuto al fatto che 
non digerisce bene la sequenza casuale dei caratteri che ho caricato nella 
variabile y.s ma non comprendo il perchè.
Infatti come verifica a ciò, ho sostituito la sequenza casuale con una 
sequenza di testo (ovvero con caratteri fuori dall'intervallo 0..31 del 
codice ascii) e tutto viene scritto correttamente.
A questo punto chiedo, perchè? A cosa è dovuto ciò?  L'oggetto TFileStream 
non dovrebbe gestire correttamente i file binari?
Grazie in anticipo per la risposta. 

 ha scritto nel messaggio 
news:TgI5g.16203$TK....@tornado.fastwebnet.it...

Dimenticavo, visualizzando con notepad il file binario "quello errato, 
ovvero quello contenente la sequenza casuale" viene mostrata una sequenza di 
quadratini anche nei campi che sono puramente testuali del record (e non 
soltanto in quei  campi del record che contengono i codici di controllo).
Cooper. 

 variabile y.s ma non comprendo il perchè.
Supponendo che sia veramente questo il problema, dovresti fornire il 
codice di questa "routine" per capire quali dati vengono caricati come 
sequenza casuale di caratteri. :-|
Ciao,
   Marco.
-- 
MARCO BREVEGLIERI
  (
http://www.marco.breveglieri.name
)


Detto, fatto.
Premesso che la struttura dati sia questa (a titolo esemplificativo):
  type pCode = Array [0..255] Of Char;
       MyRec = record
                 seq: pcode;
               end;
la funzione per la generazione casuale della sequenza di caratteri è la 
sequente:
    Function Casuale: pCode;
      Var XCount: Word;  Buffer: pCode;  X, Y: Byte;  Z: Char;
      Begin
        Randomize;
        For XCount := 0 To 255 Do Buffer [XCount] := Chr (XCount);
        For XCount := 0 To 65535 Do
          Begin
            X := RandomRange (0, 256);
            Y := RandomRange (0, 256);
            Z := Buffer [X];
            Buffer [X] := Buffer [Y];
            Buffer [Y] := Z;
          End;
        Result := Buffer;
      End;
In sostanza, viene caricato nel vettore tutta la sequenza di caratteri 
ascii, e successivamente viene mescolata, producendo una sequenza casuale 
degli stessi caratteri (che ovviamente saranno ripetuti una volta ciascuno).
La funzione incaricata di scrivere il record myrec invece è la seguente:
  Function Write (Source: String; Rec: MyRec): Byte;
    Var Err, ErrCert: Byte;  FileRec: TFileStream;
    Begin
      Err := 0;
      FileRec := TFileStream.Create (Source, fmCreate);
      Try
        Try
          FileRec.Write (rec, SizeOf (myrec));
        Except
          On Exception Do Err := 1;
        End;
      Finally
        FileRec.Free;
      End;
      Result := Err;
    End;
Ora, se nel programma principale ho:
var Info: MyRec;
begin
  Info.Seq := Casuale;
  Write ('C:\documents and settings\Impastato Marcello\Documenti\Borland 
Studio Projects\test\peppino.xcr', Info);
end.
il file viene scritto, ma riscontro quella anomalia di cui accennavo al post 
precedente, mentre se scrivo qualcosa del tipo (ad esempio):
var Info: MyRec;  vettore: pCode;  xcount: byte;
begin
  for xcount := 0 to 255 do vettore [xcount] := 'A';
  Info.Seq := vettore;
  Write ('miofile.ext', Info);
end.
il vettore viene scritto correttamente nel file; questo mi permette di 
escludere con certezza che il problema sia nella funzione WRITE e che quindi 
dipenda dalla funzione CASUALE, ma la funzione CASUALE si limita 
semplicemente a definire il contenuto del vettore esattamente come fa 
l'istruzione:
  for xcount := 0 to 255 do vettore [xcount] := 'A';
il che mi porta a pensare che mi trovo davanti a un bug; infatti, per 
completezza aggiungo che il fenomeno si ripete al 99% mentre per quel 0,01 
sembra che sia ok. In altre parole è come se funzionasse correttamente.
Questo dettaglio alla fine mi lascia pensare che una sequenza iniziale del 
vettore tenda a mandare in tilt l'intero record.
Infatti, se al record myrec, aggiungiamo altri record anche semplici e anche 
solo di testo, questi record conterranno anche la stessa anomalia del primo 
record, ovvero non conterranno il dato inserito ma ancora una volta una 
sequenza di quadratini...
Grazie ancora, spero di ottenere una delucidazione in merito a questo strano 
comportamento.
Cooper. 

 sequenza di quadratini...
Devi tenere conto del fatto che la tua routine genera anche caratteri di 
controllo, incluso il carattere nullo.
Ad ogni modo, l'esempio che hai postato è diverso dall'originale: hai 
testato anche quest'ultimo, oppure solamente il primo che hai proposto 
nel messaggio con cui hai dato inizio alla discussione?



Le verifiche le ho fatte su questo esempio specifico. Nell'altro ho 
generalizzato un po' di piu; ma il problema si presenta esattamente in 
questa circostanza specifica.
Quindi come si può rimediare? purtroppo devo utilizzare tutto il set di 
caratteri ascii, e quindi anche quelli di controllo.
Grazie ancora,
Cooper. 

    End;
invece di scrivere nel file il record rec prova a scrivere solo l'array:
FileRec.Write(rec.seq,SizeOf(rec.seq));
Giorgio 


Purtroppo non posso seguire questa soluzione perchè il record è composto da 
piu campi, ho semplificato al massimo riportando un solo campo (composto 
nello specifico da un solo array).
Giusto per mostrare il comportamento anomalo.
Cooper.

 ha scritto nel messaggio 
news:x3K5g.16430$TK.1...@tornado.fastwebnet.it...

Capisco, allora fai attenzione all'allineamento dei record in memoria, che 
puo' essere impostata a 1,2,4, Byte ecc.
Proverei solo ridefinendo i "record" come "packet record" per vedere cosa 
succede,
Giorgio

 laboriosa.
Non devi necessariamente verificare la presenza del carattere nel 
vettore: puoi usare appositamente un *set*.
Per il problema principale, dovrei fare qualche prova tecnica per 
riprodurre il difetto.
Eventualmente, cerca di inizializzare il vettore usando un solo 
carattere per tutti gli elementi e generando più file differenti, 
verificando con quale carattere il procedimento fallisce.
Dal carattere, o dai caratteri incriminati, magari è possibile trarre 
qualche considerazione.



Il carattere con cui fallisce sembra essere il #0, infatti se questo è il 
primo  e gli altri sono tipo tutte A come ad esempio:
a =  [ #0, #65, #65, #65, #65 ]
va a sostituire i successivi caratteri del vettore con caratteri di 
controllo, mentre se il primo è diverso da #0, appare il "quadratino" ma i 
successivi caratteri del vettore sono quelli effettivi.
Tenendo conto delle funzioni citate nei post precedenti, ho effettuato il 
test col codice seguente:
var Info: MyRec;    xcount, ycount: byte;
begin
  for xcount := 0 to 255 do
   begin
     info.seq [0] := chr (xcount);
     for ycount := 1 to 255 do info.seq [ycount] := 'A';
     Write (inttostr(xcount)+'.xcr', Info);
   end;
esso crea 256 files, nominati rispettivamente 0.xcr, 1.xcr, etc etc.
Ciascun file, contiene il vettore. il carattere di posizione zero del 
vettore contiene il corrispondente ascii del nome del file, e tutta una 
sequenza di caratteri (in questo caso, una sequenza di A).
Con eccezione del file 0.xcr, tutti gli altri appunto sembrano comportarsi 
correttamente (specifico: sembrano).
Nel file 0.xcr infatti noto che il primo e i successivi sono tutti 
quadratini al contrario degli altri file.
Come mai?  A cosa è dovuto quindi ciò e come si può risolvere?
Grazie ancora, Cooper.

Effettuando altre analisi ho "scoperto" che il problema non dipende neanche 
dalla funzione CASUALE che avevo scritto.
Riepilogando; la struttura dati è cosi definita:
  type pCode = Array [0..255] Of Char;
       MyRec = record
                 seq: pcode;
               end;
la funzione per scrivere il record è cosi definita:
  Function Write (Source: String; Rec: MyRec): Byte;
    Var Err, ErrCert: Byte;  FileCertWrite: TFileStream;
    Begin
      Err := 0;
      FileCertWrite := TFileStream.Create (Source, fmCreate);
      Try
        Try
          FileCertWrite.Write (rec, SizeOf (myrec));
        Except
          On Exception Do Err := 1;
        End;
      Finally
        FileCertWrite.Free;
      End;
      Result := Err;
    End;
Se ho:
const y = 251;
var Info: MyRec;  ycount: byte;
begin
  info.seq [0] := #0;
  for ycount := 1 to Y do info.seq [ycount] := 'A';
  Write ('poppo.xcr', Info);
end.
per i valori di Y <= 251 il problema non si presenta, sembra apparentemente 
che scriva il record correttamente (sottolineo apparentemente).
Per Y > 251 invece ponendo:
info.seq [0] = #0
trasforma tutti gli altri caratteri del vettore in "quadratini" ovvero 
assegna caratteri compresi nell'intervallo 0..31 del codice ascii.
Inutile dire che prima del "write" provando a leggere il vettore questo 
viene letto correttamente, ovvero che i caratteri sono quelli che devono 
essere.
Il che mi lascia pensare a un bug dell'oggetto TFileStream, confermate?
Se modifico la struttura dati in:
  type pCode = Array [0..260] Of Char;
       MyRec = record
                 seq: pcode;
               end;
(aggiungo 5 elementi in piu al vettore), ecco che di incanto il problema 
sparisce.
Qualche spiegazione logica a ciò?

 Qualche spiegazione logica a ciò?
La mia è solo una supposizione - magari sbagliata, non ho approfondito-
ma ti stai complicando la vita mischiando tipi con tutte le loro 
peculiarita'.
Non ho letto tutto il thread, ma non è più semplice utilizzare
delle shortstring (che sono array di caratteri), dei set e dei
semplici "file of" per memorizzare quello che hai bisogno?
Ciao
-- 
ACSH
[D4]
Chi fa da se', fatica il doppio

"ACSH" <x...@xxx.xxx> ha scritto nel messaggio 
news:u2N5g.106106$PR2.1...@twister2.libero.it...

Non so cosa intendi, però io nella forma originale mi ritrovo un record di 
campi; alcuni di questi campi sono a sua volta composti da record; altri 
invece no.
Ho isolato il problema, riducendo all'osso quanto nella forma originale e ho 
replicato l'errore che ottengo.
La struttura dati in questione (semplificata) è del tipo:
  type pCode = Array [0..255] Of Char;
       MyRec = record
                 seq: pcode;
               end;
il tipo pCode DEVE contenere tutti e 256 caratteri del codice ascii, nessuno 
escluso (quindi anche il #0). Avrei potuto usare il tipo string, nella 
forma: STRING [255] ma pur non risolvendo il problema, avrei dovuto sempre e 
comunque rinunciare a un carattere del codice ascii, e questo ovviamente non 
è possibile.
Quindi cercavo una soluzione a questo problema, mi viene piuttosto difficile 
pensare a una risposta del tipo: "non c'è rimedio".
Perchè finchè tutto è in memoria funziona a meraviglia, ma nel momento che 
trasferisco su file (memorizzo) e quindi rileggo, ecco che sorge il 
problema, che ovviamente non deve esserci.
Cooper.

 difficile pensare a una risposta del tipo: "non c'è rimedio".
Invece di memorizzare il _char_ memorizza il _byte_  corrispondente e usa la
funzione chr solo quando leggi il file ottenuto.


>
Dal momento che sembra implicati il TFileStream proverei ad utilizzare un 
suo simile, il TMemoryStream.
Al create non richiede alcun parametro, dopo un .Clear usi allo stesso modo 
il .Write per scrivere il record e per ottenere il file
usi il suo metodo .SaveToFile(NomeFile).
Ciao Giorgio 

 controllo).
Se continui a visualizzare con un editor di testi non vedrai mai 
correttamente il risultato. Sii più sicuro e usa uno strumento di controllo 
corretto per fare il test al tuo codice. Insomma, scrivi un programma per il 
test, anziché affidarti a notepad come se stessi scrivendo solo del testo.
Ciao
Giuseppe

 ha scritto nel messaggio 
news:445842c2$0$14784$4faf...@reader4.news.tin.it...

Giustissimo, mi sn accorto proprio grazie a questo metodo che mi sono 
accorto che qualcosa nn tornava e ho cercato di comprendere meglio 
attraverso un controllo visivo.
Dopotutto, essendo il codice ascii composto da caratteri di controllo, 
testuali e grafici, si è vero che quelli di controllo sono mostrati come 
quadratini, che alcuni sovrascrivono le righe etc etc ma è anche vero che i 
caratteri testuali e grafici se non tutti, almeno in parte vengono 
visualizzati correttamente.
Ora nel momento in cui visualizzo solo e soltanto quadratini e nessun altro 
carattere testuale/grafico, appare ancora piu evidente che qualcosa non va.
Cmq per la cronaca sto provando a effettuare i test modificando la struttura 
dalla forma:
x = array [0..255] of char
a:
x = array [1..256] of char
basandomi sul suggerimento dato da un partecipante di cui adesso non ricordo 
il nome :((( (e mi scuso) vediamo che succede.
Grazie ancora,
Cooper. 

 quadratini,
Quei caratteri non vengono *mostrati* ma *interpretati*.
Non utilizzare un editor di testi, usa un visualizzatore esadecimale di 
file, com'era una volta pc-tools per ms-dos.
Non ti ci vuole molto a farti una rappresentazione in esadecimale di 
qualsiasi file. Prendi in ingresso il tuo TFileStream e restituisci una 
stringa o una stringlist con i valori esadecimali scritti in chiaro senza 
caratteri di controllo.
Anzi, te la scrivo qui. Eventualmente correggila.
Function BinDataToString(aSource: TStream): string;
var
b: byte;
begin
Result:= '';
With aSource do
begin
Position:= 0;
While Position<Size do
begin
Read(b, 1);
Result:= Result+ IntToHex(b,2)+ ' ';
end;
end;
End;

 ha scritto nel messaggio 
news:44587f5e$0$29736$4faf...@reader2.news.tin.it...

Grazie mille, l'ho adattata un alle mie esigenze, mi ha dato un ulteriore 
riscontro a quanto già sospettavo, per cui il problema rimane :(
Cooper. 

 riscontro a quanto già sospettavo, per cui il problema rimane :(
In base alla mia esperienza il TFileStream non ha alcun problema di 
salvataggio o caricamento di file.
Puoi salvare il record così (più basso di questo poi ci sono le chiamate 
dirette alle api di windows):
H:= FileCreate('prova.dat');
FileWrite(H, TuoRecord, SizeOf(TuoRecord));
FileClose(H);
NB. stai molto attento a non inserire STRING nel tuo record! Non ti verranno 
salvate mai e poi mai. Ti salverebbe solo il puntatore. Stesso discorso con 
array dinamici.
Se non ti funziona manco così, cerca il tuo problema in un'altra parte del 
codice.


Se ho capito bene soltanto array di caratteri? giusto?
Cooper. 

 Se ho capito bene soltanto array di caratteri? giusto?
Perché, gli hai messo dentro una stringa?
Un array di caratteri va bene, ma non un array of char.
Leggi questo. Queste variabili dentro il tuo record no. Altrimenti devi 
cambiare il metodo di salvataggio.
  Object Pascal Language Guide
Dynamic variables
You can create dynamic variables by calling the GetMem or New procedure. 
Such variables are allocated on the heap and are not managed automatically. 
Once you create one, it is your responsibility ultimately to free the 
variable's memory; use FreeMem to destroy variables created by GetMem and 
Dispose to destroy variables created by New. Other standard routines that 
operate on dynamic variables include ReallocMem, Initialize, StrAlloc, and 
StrDispose.
Long strings, wide strings, dynamic arrays, variants, and interfaces are 
also heap-allocated dynamic variables, but their memory is managed 
automatically.