Il problema è che in Gambas non esistono le stringhe a lunghezza fissa, quindi bisogna operare ... riempiendo i caratteri inutilizzati in ogni stringa con caratteri fittizi per portarla ad una lunghezza prestabilita e per leggere/scrivere un determinato record spostarsi con Seek all'interno del file ....
Ecco, una cosa che manca a Gambas e che secondo me poteva essere comoda...
Non sono d'accordo con questa opinione di TopFuel per le ragioni che qui espongo.
1) Non bisogna dimenticare che qua si opera sostanzialmente con la memoria.
2) I caratteri fittizi risultano essere utili, a mio avviso, solo quando si devono gestire gruppi di dati la cui dimensione complessiva non è sempre la stessa. Poniamo il caso di dover leggere più "gruppi" significativi di dati da un file, il cui numero all'interno di uno o più gruppi, appunto, può variare di volta in volta.
La domanda che si pone è: dove finisce ogni gruppo di dati che presi in quella quantità ha un sigificato per il programma ?
In tal caso uno stratagemma può essere quello di prevedere l'inserimento, in fase di loro scrittura nel file, di un valore fittizio.
3) Il caso della modalità "Random" in VB.
Questa procedura del modo "Random" del VB mi pare che sia sostanzialmente fondata sull'uso di una specie di Struttura, nella quale va impostata la dimensione di ciascuna stringa di dati, probabilmente riservando una quantità di memoria stabilita dal programmatore, in modo tale che ogni volta verrà letto dal flusso un numero - appunto - "fisso", ben determinato, di dati a cominciare da un preciso byte del flusso.
L'unico automatismo (e quindi l'eventuale comodità) risiede nella circostanza che richiamando un membro di quel tipo personalizzato di variabile (...una specie di Struttura), è VB che sposta (probabilmente servendosi della funzione fseek() di C ) il puntatore interno del flusso al byte stabilito.
- Possibili soluzioni in Gambas -
In Gambas l'assenza di una esplicita funzionlità definita come stringa "a lunghezza fissa" può essere colmata non solo nel modo indicato dall'utente naderit qui per la lettura: http://www.gambas-it.org/smf/index.php?topic=830.msg8981#msg8981
ma anche meglio utilizzando i potenti metodi .Read() e .Write() delle variabili vettoriali.
In entrambi i casi, qualora sia necessario spostarsi in modo arbitrario con il puntatore interno del flusso, si utilizza - come ha già suggerito pastrank - la funzione Seek (...che non è un mostro informatico, giacché esiste analogamente anche in C).
La funzione Seek consente di operare in modo non usuale, arbitrario, rispetto alla modalità normale della consequenzialità dello spostamento del puntatore interno al flusso di dati. Quando noi leggiamo o scriviamo in un flusso di dati (rappresentato da variabili di tipo File o Stream) il puntatore interno si sposta automaticamente in avanti per un numero di byte pari alla quantità di memoria occupata dal tipo di variabile (Byte, Short, Integer, etc) utilizzato in quella lettura o scrittura, ponendosi sul primo byte immediatamente successivo.
Esempio in lettura:
Public Sub Main()
Dim fl As File
Dim bb As Byte[]
fl = Open "/percorso/del/file/da/leggere" For Read
With bb = New Byte[Lof(fl)]
.Read(fl, 0, 9)
End With
Print bb.ToString(0, 9)
Seek #fl, 8
bb.Read(fl, 0, 10)
Print bb.ToString(0, 10)
fl.Close
End
Ma come la mettiamo se leggiamo un record, ne cambiamo la lunghezza e poi lo riscriviamo sul file? Sopratutto se non è l'ultimo e sopratutto se è diventato più lungo perchè magari abbiamo allungato una stringa aggiungendo delle parole? Quel record andrebbe a sovrascrivere il successivo, a meno che Gambas non vada automaticamente a spostare in avanti i record successivi (dubito) o lo facciamo noi da programma (complicato).
Aspetto la risposta di Vuott.
sanmidi
O San Midi (http://it.wikipedia.org/wiki/Musical_Instrument_Digital_Interface), musico ed anacoreta,
tu che puoi e dai soddisfazione,
deh, sciogli ogni dubbio, limite d'ogni meta.
Dammi tu la soluzione:
io m'affido solo a te,
io m'affido solo a te !
Rispondo con un esempio pratico.
Poniamo il caso di avere un file di testo, nel quale vi sono i seguenti caratteri:
abcde123456ABCDE
Tale stringa di testo è costituita da 3 gruppi (record) significativi:
abcde
123456
ABCDE
Decidiamo di aggiungere al secondo gruppo significativo ad esempio i seguenti 3 caratteri: )*+
Come auspicato da TopFuel, l'inserimento/aggiunta dei 3 caratteri non deve coprire quelli del terzo gruppo (record). Inoltre la procedura non deve essere complicata (...che significherà ??? )
Comunque sia ecco l'esempio:
Public Sub Main()
Dim fl As File
Dim bb, cc As Byte[]
' Individuiamo i caratteri da aggiungere:
cc = [41, 42, 43]
' Apriamo in lettura ed in scrittura al quale aggiungere i tre byte/caratteri:
fl = Open "/percorso/del/file" For Read Write
' Creiamo ed istanziamo il vettore che accoglierà i dati letti dal file:
With bb = New Byte[Lof(fl)]
' Leggiamo il file:
.Read(fl, 0, bb.Count)
' Ecco il metodo che ci consente di inserire agevolmente e con sicurezza uno o più dati nella stringa.
' Gambas sposterà automaticamente in avanti i record successivi (in questo caso l'ultimo):
.Insert(cc, 11)
' Poiché la lettura dei dati nel file, precedentemente effettuata, ha comportato uno spostamento automatico
' del puntatore di posizione in avanti nel file, lo riposizioniamo all'inizio, per poter riscrivere nel file la nuova stringa di caratteri:
Seek #fl, 0
.Write(fl)
End With
' Chiudiamo il fusso di dati e... accendiamo un cero di ringraziamento a San Midi:
fl.Close
End
Se vuoi evitare il Seek ed abbreviare il codice si può fare così:
Public Sub Main()
Dim bb, cc As Byte[]
cc = [41, 42, 43]
With bb = Byte[].FromString(File.Load("/percorso/del/file"))
.Insert(cc, 11)
File.Save("/percorso/del/file", String@(.data))
End With
End
come la mettiamo se leggiamo un record, ne cambiamo la lunghezza e poi lo riscriviamo sul file? Sopratutto se non è l'ultimo e sopratutto se è diventato più lungo perchè magari abbiamo allungato una stringa aggiungendo delle parole?
Se usi record a lunghezza fissa, perche' vuoi metterne uno piu' lungo? Se tieni la lunghezza fissa, puoi permetterti di leggere con metodi tipo
# scritto a memoria, non ho gambas su questo pc
fn = Open nomedelfile For Read
for i = 0 to lof(fn) / lunghezzadelfile -1
Seek #fn, 1000 * i
Read #fn, x, 10
next
Close #fn
e scrivere allo stesso modo: se tieni i record lunghi, non hai problemi con la lunghezza della stringa, la termini con un carattere tipo tab e quando vai a rileggerla tutta la tagli su quello.
Type MyRecord
LastName as String*20
FirstName as String*20
Age as Integer
Married as Boolean
End Type
' LastName richiede 20 byte, FirstName richiede 20 byte, età richiede 2 byte e Married richiede 2 byte, in modo che il totale è 20 + 20 + 2 + 2 = 44.
Scusate tutti quanti ma a causa della niubbaggine stento a ben spiegare quello che intendo dire.
Io intendevo più o meno questo (rimanendo in tema VB):
Private $sNome As String ' 20 = 19 + tab
Private $sCognome As String ' 20 idem
Private $sEta As String ' 2 + tab
Private $sSposato As String ' 1 + tab
Private $nLungo As Long
Private Const lunghezzadelfile As Byte = 45
Public Sub Form_Open()
If Exist(User.Home &/ Application.Name &/ "mioFile.dat") Then
$nLungo = lungo()
SpinBox1.MaxValue = $nLungo
Else
SpinBox1.MaxValue = 1
Endif
SpinBox1.MinValue = 1
Me.Center
End
'' SCRIVE
Public Sub Button1_Click()
Dim fl As File
Dim nomedelfile As String
If TextBox1.text = "" Or TextBox2.Text = "" Or TextBox3.Text = "" Or TextBox4.Text = "" Then
Message.Info("Impossibile aggiungere record se i dati non sono completi")
Return
Endif
$sNome = fissaScrivo(TextBox1.Text)
$sCognome = fissaScrivo(TextBox2.Text)
$sEta = eta(TextBox3.Text)
$sSposato = sposi(TextBox4.Text)
If Not Exist(User.Home &/ Application.Name &/ "mioFile.dat") Then
Mkdir User.Home &/ Application.Name
Endif
nomedelfile = User.Home &/ Application.Name &/ "mioFile.dat"
If Not Exist(nomedelfile) Then
fl = Open nomedelfile For Create
Else
fl = Open nomedelfile For Write 'Append
$nLungo = lungo()
Endif
Seek #fl, $nLungo * lunghezzadelfile
Write #fl, $sNome & $sCognome & $sEta & $sSposato
fl.Close
Inc $nLungo
SpinBox1.MaxValue = $nLungo
TextBox1.Text = ""
TextBox2.Text = ""
TextBox3.Text = ""
TextBox4.Text = ""
End
Private Function lungo() As Long
Dim fl As File
Dim nomedelfile As String
nomedelfile = User.Home &/ Application.Name &/ "mioFile.dat"
fl = Open nomedelfile For Read
Return Lof(fl) / lunghezzadelfile
End
Private Function fissaScrivo(valore As String) As String
Dim i As Integer
If Len(valore & "\t") < 20 Then
i = 20 - Len(valore & "\t")
Return valore & "\t" & Space$(i)
Else If Len(valore & "\t") = 20 Then
Return valore & "\t"
Else If Len(valore & "\t") > 20 Then
Return Left$(valore, 19) & "\t"
Else
Quit
Endif
End
Private Function eta(valore As String) As String
If IsNumber(valore) Then
Return valore & "\t"
Else
Message.Info("L'età deve essere un numero")
TextBox3.Text = ""
TextBox3.SetFocus
Endif
End
Private Function sposi(valore As String) As String
If valore = "S" Or valore = "N" Then
Return valore & "\t"
Else
Message.Info("Immettere una S per si oppure una N per no")
TextBox4.Text = ""
TextBox4.SetFocus
Endif
End
'' LEGGE
Public Sub Button2_Click()
Dim i As Integer
Dim fl As File
Dim nomedelfile, s As String
Dim sDati As String[]
nomedelfile = User.Home &/ Application.Name &/ "mioFile.dat"
fl = Open nomedelfile For Read
i = CInt(SpinBox1.Text) - 1
Seek #fl, lunghezzadelfile * i
s = Read #fl, lunghezzadelfile
sDati = Split(s, "\t", "", True, False)
$sNome = Trim$(sDati[0])
$sCognome = Trim$(sDati[1])
$sEta = Trim$(sDati[2])
$sSposato = Trim$(sDati[3])
TextBox1.Text = $sNome
TextBox2.Text = $sCognome
TextBox3.Text = $sEta
TextBox4.Text = $sSposato
End
Questa più o meno la Form main (vedi istantanea allegata)
:Ciao:
@Top Fuel e @Vuott
E' proprio in questi due post a seguire che spiego l'uso delle stringhe...
Dovuto al fatto che non capisco niente di stream...
Scusate se riprendo la discussione con apparente ritardo ma ero rimasto in attesa di eventuali vostri nuovi post.
Io fin'ora se dovevo memorizzare dei dati in una mia applicazione ricorrevo ad un database.
Sono pertanto digiuno di quanto qui voi siete a discutere e anticipo le mie scuse per eventuali castronerie.
...
Quindi se fosse scritto su un file di testo sarebbe come una roba così:...
...
...
Scusate tutti quanti ma a causa della niubbaggine stento a ben spiegare quello che intendo dire.
Io intendevo più o meno questo (rimanendo in tema VB):
...
… intendevo riferirmi sempre a “se fosse un file di testo”...
...
1- Non è obbligatorio che tutti i campi debbano essere riempiti, si può anche lasciarli vuoti.
Ma non sei stato tu a parlare di un modo per poter scrivere cose più lunghe senza dover intervenire su quanto scritto dopo? Il fatto che poi io non ne abbia tenuto conto nel codice non inficia l'esempio che potrebbe essere sviluppato.
Si potrebbe leggere dato per dato, inteso come campo, se ci sono cambiamenti ecc... Bada bene non sto parlando di codice efficiente ma di possibilità.
Ribadisco il mio è un esempio con stringhe per spiegare in pratica cosa chiedevo in aiuto.
Comunque mi rimane difficile capire poi come fare a utilizzare una forma di split se non usiamo le stringhe.
2- Per eliminare un singolo record non c'è bisogno di riscrivere tutto il file, basta spostare di una posizione indietro tutti i record seguenti.
Magari mi si spiegasse come fare! :rolleyes:
3- Perchè non hai usato una struttura?
A parte che io di strutture ne capisco punto, comunque ti ha già risposto vuott
Sarebbe molto gradita versione aggiornata e corretta del mio esempio
Per il resto direi un buon lavoro. :)
Grazie lo stesso, anche se è una pietosa bugia :-*
3- Perchè non hai usato una struttura?
...effettivamente questa è una domanda giusta e che è sorta anche in me.
Ma forse la risposta sta nel fatto che Gianluigi utilizza solo variabili di tipo Stringa, quindi di tipo omogeneo; i valori delle quali vengono poi raccolti insieme da un vettore di tipo Stringa:
Però è proprio qua che mi sorge un'altra domanda per lui:
Perché utilizzare esclusivamente variabili di tipo Stringa ?
Se per esempio, come nel suo programma, io devo inserire 10 valori "età", questi naturamente potranno essere rappresentati ciascuno da un solo byte.
Pertanto, si potrebbe utilizzare per ciascun valore dell'età una variabile di tipo Byte, ...la quale occupa 1 byte di memoria.
La sola variabile Stringa invece occupa ben 8 byte !!!
Spiegazione già data circa l'uso delle stringhe a parte, cosa intendi parlando di 8 byte? Le stringhe (al di sotto delle 127 lettere) non occupano un byte ogni carattere?
Intendi dire che se si utilizzasse una struttura si potrebbe usare lo split limitatamente alle due stringhe in quanto poi età e booleano occuperebbero un solo byte ciascuno?
Sarebbe una cosa del genere:
Public Struct mioRecord
sNome as String ' 19 + 1 (tab) con supporto di una funzione.
sCognome as String ' Idem
byEta as Byte ' 1 byte
bSposi as Boolean ' 1 byte
End Struct
Per un totale di 42 byte invece di 46? Ma si può usare split nelle strutture? A me i tipi creati dall'utente sono sempre apparsi inutili (evidentemente non li capisco proprio).
Poi ci si sposta sempre con Seek di 42 in 42?
Grazie dell'attenzione.
:ciao:
cosa intendi parlando di 8 byte? Le stringhe (al di sotto delle 127 lettere) non occupano un byte ogni carattere?
La lunghezza dipende da molti fattori, tra i quali il formato del codice macchina della CPU destinazione.
Se scriviamo:
dim s as string = "UNA-STRINGA-LUNGA"
print s & s
consuma MENO memoria di
print "UNA-STRINGA-LUNGA" & "UNA-STRINGA-LUNGA"
Se il compilatore NON capisce che nella print ci sono due costanti letterali uguali, nel codice (o altrove) dovrà inserire le due costanti, separatamente. Ognuna occupa almeno 18 bytes (17 per i dati, +1 almeno, o 4 o 8 a seconda della CPU, per la lunghezza).
Nel frammento precedente, invece, la stringa è specificata una volta sola; poi, ogni volta che la si usa, bastano 8 byte (un Puntatore) in un sistema a 64bit per dire dov'è la stringa e usarla.
Ma tieni presente che "print s & s" PRIMA concatena la stringa (quindi arriviamo a 42 bytes), poi la stampa. Quindi a un certo momento abbiamo bisogno di 42+18 bytes nel primo frammento, e 42+18+18 nel secondo frammento.
Una stringa di ad esempio di 80 caratteri ha bisogno di almeno 81 caratteri, che possono diventare 84, 88, 92 o giù da lì per questioni tecniche. Il numero "5" invece può occupare 1 byte se lo dichiari come byte, oppure 2 oppure 4 oppure 8 oppure 10 - dipende dal contesto.
Sicuramente un puntatore o una variabile tengono meno posto di una stringa di 80 caratteri, ecco quello che volevo dire.
:rolleyes:
Comunque sia, prova questo:
Public Sub Main()
Print SizeOf(gb.String)
End
Nelle strutture ogni campo occupa la lunghezza della variabile che la rappresenta, quindi, a parte le stringhe e i variant, le variabili numeriche occupano 1, 2, 4 o 8 byte.
Nelle strutture non c'è bisogno di nessuno split, basta usare i campi:
....
Più semplice di così... :)
Uhmmm.... non è poi così tanto semplice. Ed infatti, proprio per questo, io non ho sollecitato un uso delle Strutture.
VB usa sostanzialmente delle Strutture, ma poi ha "dietro le quinte" senz'altro una procedura per gestirle al meglio in fase di scrittura e soprattuto di lettura da un file.
In vero, la scrittura in un file dei dati presenti in una Struttura può vedere una procedura un po' complessa, scrivendo i dati membro per membro (un po' come ha fatto sopra Top Fuel):
Write #hFile, variabileStruttura.sNome
Write #hFile, variabileStruttura.sCognome
etc...
oppure può vedere un'unica, breve, sintentica istruzione:
Write #hFile, variabileStruttura As miaStruttura
Ma il processo della lettura dei dati dal file con successivo riempimento dei membri della Struttura, prevederà necessariamente l'uso della funzione Seek per riempirli uno ad uno.
...in vero si potrebbe ottenere una lettura più breve con riempimento in un'unica soluzione della Struttura, ma è procedura delicatissima, dovendosi prestare grande attenzione per gli eventuali necessari "allineamenti" dei dati all'interno dell'area di memoria riservata dalla Struttura medesima !
Quindi Seek non tiene conto...
Se dovessi usare una struttura? Come uso Seek come faccio a dimensionarlo e a spostarmi con esso?
La funzione Seek agisce all'interno di un flusso di dati rappresentati dalla variabile di tipo File oppure Stream, non all'interno di una Stringa di caratteri.
Con la Struttura quella funzione va usata.... come si usa normalmente. ;D
Esempio.
Poniamo il caso di avere un file lungo 32 byte. All'interno vogliamo scriverci i dati di una Struttura.
In particolare vogliamo che i dati del 3° membro della nostra Struttura siano scritti non sequenzialmente rispetto ai due mebri che lo precedono, bensì al 17° byte (indice 16):
Public Struct STRUTTURA
b a Byte
s As Short
i As Integer
End Struct
Public Sub Main()
Dim st As New STRUTTURA
Dim fl As File
With st
.b = 9
.s = 999
.i = 99999
End With
fl = Open "/percorso/del/file" For Write
Write #fl, st.b As Byte
Write #fl, st.s As Short
' Scriviamo i dati dell'Intero a cominciare dal byte di indice 16 del file:
Seek #fl, 16
Write #fl, st.i As Integer
fl.Close
End
...volevo capire come si deve misurarne lo spostamento, io credevo si misurasse in byte....
Lo spostamento lo imposti tu su un indice.
Ponendo sempre il caso di un file formato da 32 byte.....
voglio leggere un byte del file e precisamente il 23°:
Dim b As Byte
...
Seek #hFile, 22 ' il 23° byte è ovviamente il byte di numero indice 22 !
Read #hFile, b
Ora invece voglio leggere un Intero, i cui suoi 4 byte - che lo compongono - cominciano ad esempio dal 17° byte:
Dim i As Integer
......
Seek #hFile, 16
Read #hFile, i ' ...ovviamente, essendo un Intero, verranno letti 4 byte di seguito (l'Integer occupa "comunque" 4 byte di memoria)
Comunque... guarda, in questi casi bisogna fare le prove: (http://forum.ubuntu-it.org/images/smilies/iospalo.gif) sbagliare, imprecare, riprovare.
Rispetto a SEEK: A ben guardare nella documentazione online, non si parla apertamente di byte, questo è quanto li riportato:
“Definisce la posizione del puntatore dello stream, per la successiva operazione di lettura / scrittura.
Se Position è negativo, allora il puntatore dello stream viene spostato indietro rispetto alla fine del file.
Per spostare il puntatore dello stream dopo la fine del file, è necessario utilizzare la funzione Lof. “
Però poi negli esempi c'è questo, come terzo:
' Move 100 bytes before the end of the file
SEEK #hFile, -100
e qui si parla precipuamente di spostamento indietro di 100 byte.
Pertanto ritengo di poter dire che, malgrado quanto qui precedentemente sostenuto da vuott a cui riconosco peraltro un'enorme maggiore conoscenza di programmazione in generale e di Gambas in particolare rispetto a me, lo spostamento in byte operato da SEEK nello stream corrisponde ad un byte per carattere come dimostrava già in parte la piccola applicazione postata in precedenza e come dimostra ancora meglio la nuova (RecordFisso-0.0.1) versione che qui posto e che fa solo uso di stringhe (a lunghezza fissa) e di SEEK (provata su 3 differenti configurazioni hardware).
Per favorire l'eventuale prova anche ai colleghi del forum interessati che non hanno l'ultima versione (3.6.2) stabile ma versioni precedenti la 3.5 ho messo al posto dello SpinBox una scrollbar + textbox che ne simula più o meno il funzionamento e ho rimosso lo Spinner dalla textlabel che informa l'operatore sull'elaborazione dei dati.
Questa versione è la RecordFisso-SSS-0.0.1 (senza spinbox e spinner).
In questo programma ho accolto il suggerimento di Top Fuel circa l'eliminazione dei record che ora il programma sposta in fondo, marca come cancellati e sovrascrive all'atto di nuovi inserimenti.
Questa è solo una piccola dimostrazione alla (vana?) ricerca della comprensione riguardo la scrittura di stream e l'uso di SEEK e non ha altro scopo che quello. Infatti tutto questo spostare e rispostare record nel file è un modo ben poco raccomandabile a chi avesse a cuore l'integrità dei dati.
Per facilitare le prove, il programma ha un metodo di popolamento automatico del file dati che può essere richiamato da Open togliendovi il commento.
Attenzione il metodo è tarato per un file dati da centounomila record se lo volete più piccolo in modo da poterlo poi aprire per controllarne il contenuto basta cambiare la cifra. :)
Una precisazione: Il programma crea una cartella (visibile) col suo stesso nome nella vostra home con all'interno il file dati denominato mioFile.dat.
Questo programmino è ispirato all'esempio che appare sul sito di Windows che tratta di stringhe fisse e metodo RANDOM indicato da vuott in questa stessa discussione.
Non ho usato una struttura perché fin'ora nessuno è riuscito a spiegarmene i reali vantaggi :P
Applicazioni rimosse: vedi ultimo post
:ciao:
Rispetto a SEEK: A ben guardare nella documentazione online, non si parla apertamente di byte
...malgrado quanto qui precedentemente sostenuto da vuott ..... lo spostamento in byte operato da SEEK nello stream corrisponde ad un byte per carattere
...non riesco a capire. :rolleyes:
Per byte mi riferisco ad ogni singolo dato presente nel file, nel flusso; ossia al valore composto da 8 bit, che consente 256 configurazioni di bit distinte, e che è memorizzabile in una variabile char del linguaggio C. :-\
Bene allora siamo in due a non capire... :rolleyes:
Però è proprio qua che mi sorge un'altra domanda per lui:
Perché utilizzare esclusivamente variabili di tipo Stringa ?
Se per esempio, come nel suo programma, io devo inserire 10 valori "età", questi naturamente potranno essere rappresentati ciascuno da un solo byte.
Pertanto, si potrebbe utilizzare per ciascun valore dell'età una variabile di tipo Byte, ...la quale occupa 1 byte di memoria.
La sola variabile Stringa invece occupa ben 8 byte !!! (http://levipere.com/wp-content/plugins/smilies-themer/Axendr/icon_rolleyes.gif)
...
La lunghezza dipende da molti fattori, tra i quali il formato del codice macchina della CPU destinazione.
...
Una stringa di ad esempio di 80 caratteri ha bisogno di almeno 81 caratteri, che possono diventare 84, 88, 92 o giù da lì per questioni tecniche. Il numero "5" invece può occupare 1 byte se lo dichiari come byte, oppure 2 oppure 4 oppure 8 oppure 10 - dipende dal contesto.
Sicuramente un puntatore o una variabile tengono meno posto di una stringa di 80 caratteri, ecco quello che volevo dire.
Comunque sia, prova questo:
Public Sub Main()
Print SizeOf(gb.String)
End
...
Però poi negli esempi c'è questo, come terzo:
' Move 100 bytes before the end of the file
SEEK #hFile, -100
e qui si parla precipuamente di spostamento indietro di 100 byte.
...
Credevo di essere stato sufficientemente chiaro circa quello che intendevo.
Ma evidentemente non è così e allora provo a metterla così:
Se SEEK si sposta in base ai byte di memoria come sembra da te sostenuto in questa discussione e nei tuoi esempi, ponendo come studio questo codice:
Dim b As Byte = 123 ' 1 byte
Dim s As Short = 123 ' 2 byte
Dim i As Integer = 123 ' 4 byte
Dim l As Long = 123 ' 8 byte
' avendo un file che ha memorizzato in sequenza b s i l
' sembrerebbe dal tuo ragionamento che SEEK si dovrebbe
' spostare in questo modo (tesi che io cerco di confutare).
Seek #mioFile, 0 ' dovrei essere su b
Seek #mioFile, 1 ' dovrei essere su s
Seek #mioFile, 5 ' dovrei essere su i
Seek #mioFile, 13 ' dovrei essere su l
SEEK in realtà si sposta in base alle cifre e nel caso appena esposto si dovrebbe spostare così:
0, 3, 6, 9 per trovarsi all'inizio delle cifre, almeno credo anche se non ho fatto prove in tal senso essendomi concentrato su quanto qui discusso e cioè Stringhe A Lunghezza Fissa.
:ciao:
La sola variabile Stringa invece occupa ...
Riguardo alla Stringa possiamo dire che una Stringa formata da qualsiasi quantità di caratteri, occupa *sempre* in quanto variabile quegli 8 byte, perché essa è in realtà un puntatore a una struttura più complessa (che contiene, fra l'altro, la lunghezza della stringa).
SEEK in realtà si sposta in base alle cifre e nel caso appena esposto si dovrebbe spostare così:
0, 3, 6, 9 per trovarsi all'inizio delle cifre
Bisogna distinguere lo spostamento automatico del puntatore interno del file in caso di lettura o di scrittura del file dallo spostamento del puntatore interno del file in modo... manuale: con la funzione Seek.
Insomma la funzione Seek consente di posizionare il puntatore su qualsiasi byte del file/stream.
E' chiaro che nel caso in cui, utilizzando il Seek, il puntatore interno non venga impostato sul primo byte utile per la corretta lettura o scrittura del file, il valore letto o scritto risulterà sfalzato, errato. Ciò però non toglie che io - appunto con Seek - possa leggere una parte o scrivere solo su una parte dell'intero valore sul file/stream.
Si faccia al riguardo il seguente esperimento.
Public Sub Main()
Dim fl As File
Dim i As Integer
fl = Open "/tmp/miofile" For Create
fl.Close
fl = Open "/tmp/miofile" For Read Write
Write #fl, 9999 As Short
Write #fl, 444444 As Integer
Write #fl, 9999 As Short
Seek #fl, 3
Read #fl, i
Print i, Hex(i, 4)
' Ora leggiamo il valore Intero precedentemente scritto (44444) dal suo primo byte utile:
Seek #fl, 2
Read #fl, i
Print i, Hex(i, 4)
fl.Close
End
... in Gambas in questo contesto non mi sembra che siano particolarmente utili ecco...
Mah........ diciamo che sono poco utilizzate. Questo sì.
Come puoi ben immaginare, le Strutture possono essere utilizzate anche all'interno di vettori di Strutture.
Qualche velo inizia a cadere e la nebbia a farsi meno fitta...
Guardate un po qui: :-[
Public Sub Form_Open()
scrivi
leggi
End
Public Sub scrivi()
Dim fl As File
Dim nomedelfile As String
Dim b As Byte = 123 ' 1 byte
Dim s As Short = 123 ' 2 byte
Dim i As Integer = 123 ' 4 byte
Dim l As Long = 123 ' 8 byte
Dim s1 As String = "Pippo"
Dim s2 As String = "Pluto"
If Not Exist(User.Home &/ Application.Name) Then
Mkdir User.Home &/ Application.Name
Endif
nomedelfile = User.Home &/ Application.Name &/ "mioFile.dat"
If Not Exist(nomedelfile) Then
fl = Open nomedelfile For Create
Else
fl = Open nomedelfile For Write
Endif
Seek #fl, 0
Write #fl, b As Byte
Write #fl, s As Short
Write #fl, i As Integer
Write #fl, l As Long
Write #fl, s1, 10
Write #fl, s2, 10
fl.Close
End
Public Sub leggi()
Dim fl As File
Dim nomedelfile As String
Dim b As Byte
Dim s As Short
Dim i As Integer
Dim l As Long
Dim s1, s2 As String
nomedelfile = User.Home &/ Application.Name &/ "mioFile.dat"
fl = Open nomedelfile For Read
Seek #fl, 0
b = Read #fl As Byte
's = Read #fl As Short
'i = Read #fl As Integer
Seek #fl, 7
l = Read #fl As Long
Seek #fl, 25
s2 = Read #fl, 10
fl.Close
Print b, l, s2 ' bisognerebbe scriverlo Trim(s2) ma così si comprende che ci pensa lo stesso Gambas a riempire (come fa VB o no?).
End
Posso tranquillamente affermare che praticamente tutto quello qui da me sostenuto erano delle beneamate … :'(
Sarà bene ch'io vada a studiare le strutture... :-[
Cosa mi suggerite, chiedo ai moderatori di cancellare questa discussione per pietà nei miei confronti?
Credo di aver compreso cosa significavano fin dal principio di questa discussione sia pastrank che vuott.
Anche Gambas offre le stringhe a lunghezza fissa (più o meno, in effetti devi assicurarti che la stringa non sfori), rimanendo sul tema il codice a seguire lo rende molto evidente.
Anche il mio socio coautore Top Fuel aveva le sue ragioni nel caldeggiare l'uso delle strutture che semplificano il codice.
Con questo spero di non aver ulteriormente aggravato la mia già scarsa credibilità.
Public Struct miaStruttura
Nome As String
Cognome As String
Eta As Byte
Stato As Byte
End Struct
Private Agenda As New MiaStruttura
Private nomedelfile As String
Public Sub Form_Open()
scrivi
leggi
End
Public Sub scrivi()
Dim fl As File
Dim b As Byte
' Crea un file temporaneo se già c'è
' lo elimina, se da errore esce.
nomedelfile = "/tmp" &/ "mioFile.dat"
If Exist(nomedelfile) Then Try Kill nomedelfile
If Error Then Quit
' Apre il file in creazione
fl = Open nomedelfile For Create
' popola nove record
With Agenda
For b = 0 To 8
.Nome = "Nome" & CStr(b + 1)
Write #fl, .Nome, 10
.Cognome = "Cognome" & CStr(b + 1)
Write #fl, .Cognome, 10
.Eta = 20 + b
Write #fl, .Eta As Byte
.Stato = b Mod 2
Write #fl, .Stato As Byte
Next
End With
fl.Close
End
Public Sub leggi()
Dim fl As File
Dim n, c As String
Dim b, e, s As Byte
' Apre il file in lettura
fl = Open nomedelfile For Read
' Legge i nove record e li scrive
' in console in modo che appaiano
' affiancati i dati puliti e quelli
' con i caratteri inseriti da Gambas.
For b = 0 To (Lof(fl) - 1) Step 22
Seek #fl, b
n = Read #fl, 10
c = Read #fl, 10
e = Read #fl As Byte
s = Read #fl As Byte
Print Trim(n), Trim(c), e, s, "|" & n, c, e, s
Next
fl.Close
End
...deduco dalla tua domanda che quei “sgradevoli caratteri” non rappresentano il “riempitivo” che Gambas utilizza per marcare gli spazi, cosa che io invece credevo.
E' appunto perché io credevo fossero i marca byte che li ho mostrati a fianco dei risultati corretti.
...m...marca byte ? :rolleyes:
Quei caratteri strani sono (lo si nota anche dall'aspetto grafico) la rappresentazione stringa di byte con valore 0 (zero).
Li hai ottenuti imponendo nelle righe ove scrivi con Write, come ad esempio:
e più segnatamente al terzo parametro, un valore superiore ai dati da scrivere presenti nel membro della Struttura.
Vedi anche qui:
http://www.gambas-it.org/wiki/index.php?title=Write#Scrivere_una_quantit.C3.A0_superiore_ai_dati_in_possesso
Se vuoi impostare gli spazi dopo i caratteri alfanumerici presenti nei membri della Struttura, li devi aggiungere in modo preciso e puntuale: le modalità per farlo - come sai - sono più di una.
Forse c'è un qui pro quo.
Io ho volutamente segnato più spazio stringa (10) al posto dei 5 usati per il nome e 8 per il cognome proprio per rappresentare una stringa a lunghezza fissa che possa poi all'occorrenza essere sostituita con una più lunga senza compromettere la struttura del record.
Ora io non sono così addentro a Gambas per sapere se ci sono altri modi per sostituire al meglio una stringa in un record senza sovrascrivere la stringa (o il valore) successiva, però così come ho fatto posso tranquillamente sostituire (sempre rimanendo in questo caso entro i dieci caratteri, ma ne puoi mettere quanti abbisognano) le stringhe già scritte con altre più lunghe, che poi è il tema della discussione.
Se aggiungi al codice che ho postato la routine “sostituisci”:
Public Sub sostituisci()
Dim fl As File
Dim b As Byte
' Apre il file.
nomedelfile = "/tmp" &/ "mioFile.dat"
' Apre il file in scrittura
fl = Open nomedelfile For Write
' cambia i nove record
With Agenda
For b = 0 To 8
.Nome = "NomeSost" & CStr(b + 1)
Write #fl, .Nome, 10
.Cognome = "CognSost" & CStr(b + 1)
Write #fl, .Cognome, 10
.Eta = 30 + b
Write #fl, .Eta As Byte
.Stato = 2
Write #fl, .Stato As Byte
Next
End With
fl.Close
End
E vari così il codice Form_Open:
Public Sub Form_Open()
scrivi
leggi
sostituisci
leggi
End
Dovresti vedere che cosa intendo dire.
Avevo visto quanto mi hai segnalato ma così come ho fatto mi sembra funzionare bene.
Io ho volutamente segnato più spazio stringa (10) al posto dei 5 usati per il nome e 8 per il cognome proprio per rappresentare una stringa a lunghezza fissa che possa poi all'occorrenza essere sostituita con una più lunga senza compromettere la struttura del record.
Capisco.
Ad ogni modo dovresti trovare la maniera per evitare che quei caratteri di riempimento vengano eventualmente riportati e scritti in console.
Allora proprio non riesco a spiegare ciò che voglio dire:
Nel codice ho scritto:
'…
' affiancati i dati puliti e quelli
' con i caratteri inseriti da Gambas.
For b = 0 To (Lof(fl) - 1) Step 22
Seek #fl, b
n = Read #fl, 10
c = Read #fl, 10
e = Read #fl As Byte
s = Read #fl As Byte
Print Trim(n), Trim(c), e, s, "|" & n, c, e, s
Next
Se il codice ti funziona correttamente Print ti dovrebbe stampare la colonna pulita (basta usare Trim) e a destra, separata da una barra verticale, quella nature realmente presente nel file e che mostra visivamente cosa fa Gambas dietro le quinte per “marcare lo spazio” della stringa “a lunghezza fissa”.
E' una cosa voluta per aiutare nella comprensione, ma sembra che invece di aiutare confonda, cosa che vorrei evitare.
Sono a chiederti, ammesso che quanto da me scritto non sia errato, come posso fare per rendere il mio discorso comprensibile a tutti?
Se il codice ti funziona correttamente Print ti dovrebbe stampare la colonna pulita (basta usare Trim) e a destra, separata da una barra verticale, quella nature realmente presente nel file e che mostra visivamente cosa fa Gambas dietro le quinte per “marcare lo spazio” della stringa “a lunghezza fissa”.
E' una cosa voluta per aiutare nella comprensione
Va bene. Ora ho capito lo scopo.
Va bene.
Sono contento di essere riuscito a spiegarmi, ma con te è senz'altro più semplice perché di programmazione ne sai, ma con uno che ne sa ancora meno di me...
Penso che parte dell'incomprensione sia dovuta anche al fatto che io mi esprimo in modo non scientifico e poi giustamente la mia credibilità come programmatore... :)
Gradirei capire se questo tuo va bene si limita al fatto che ora sono riuscito a spiegarmi oppure sei più o meno d'accordo con quanto sostengo?
Occorre una precisazione su quanto ho fin qui detto: se si opera una sostituzione mirata al singolo dato del record questa va fatta portando il SEEK sul primo byte del campo da sostituire, è già stato detto da te ma è meglio ribadirlo perché quanto scritto ultimamente potrebbe aver confuso le acque.
Per verificare con mano cosa intendo si può aggiungere al codice di esempio quest'altra routine:
Public Sub sostituisciUno()
Dim fl As File
' Il percorso
nomedelfile = "/tmp" &/ "mioFile.dat"
' Apre il file in scrittura
fl = Open nomedelfile For Write
' cambia il secondo e quarto campo del terzo record
' prima si deve portare sul terzo record il n°2 partendo
' da zero deve pertanto sommare (22*2) + 10
Seek #fl, (22 * 2) + 10
With Agenda
.Cognome = "CogSingle3"
Write #fl, .Cognome, 10
' e poi sul quarto
Seek #fl, (22 * 2) + 10 + 10 + 1
.Stato = 118
Write #fl, .Stato As Byte
End With
fl.Close
End
Cambiando cosi Form_Open:
Public Sub Form_Open()
scrivi
leggi
sostituisci
leggi
sostituisciUno
leggi
End
Ho rimosso le applicazioni che avevo postato in quanto fuorvianti partendo da presupposti sbagliati.
Invero io le avevo postate nella speranza che qualcuno si prendesse la briga di cambiarle la dove serviva.
Trovo più utile chiudere i miei interventi con quest'ultima precisazione che comunque già appare nelle spiegazioni della wiki (che a mio avviso andrebbe rivista in parte) ma che non avevo inteso nella sua reale portata.
Quando come nel codice postato scriviamo l'istruzione
in realtà facciamo esattamente la stessa cosa che in VB si fa scrivendo
infatti se noi assegniamo un cognome più corto sia Gambas che VB lo riempono dei caratteri mancanti fino a portarlo a 10 totali e al contrario se noi dovessimo assegnare un cognome più lungo lo troncherebbero. Per poterlo verificare in Gambas basta cambiare questa riga della routine “sostituisciUno” appena sotto “With Agenda” con
.Cognome = "CognomeSingle3"
per constatare come il cognome, essendo più lungo di 10 caratteri, venga troncato automaticamente e non vada a sovrascrivere il valore successivo.
Altra precisazione, sono ritornato alle mie posizioni di partenza riguardo le strutture, non mi riesce proprio di capire a cosa servano, se infatti avete voglia di cambiare il codice togliendo la struttura (cancellandone tutte le tracce) lasciando solo le variabili vi accorgereste che il codice lavora nello stesso identico modo.
Mia riflessione finale: Ringrazio chi ha postato il quesito e chi vi ha partecipato ora comunque ho una conoscenza più ampia dei file mi si è diradata (in piccola parte) la nebbia riguardo gli stream ho una vaga percezione in più riguardo i puntatori.
Ma ora con maggiore consapevolezza di prima ringrazio ancora di più chi mi mette a disposizione database di tutte le taglie che mi permettono di trattare in sicurezza dati che se lasciati a me e ai file andrebbero sicuramente a ramengo.
:ciao:
Riprendo questa discussione ed in particolare la parte relativa alle Strutture.
E' interessante come Minisini abbia ultimamente in una sua risposta nella Mailing List ufficiale espresso questo giudizio sull'uso delle Strutture in Gambas:
" Note that internally the structure Elements is transformed into a
temporary object pointed to the real data so that you can use the syntax
'Element.XXX'.
In other words, unless you talk to a C shared-library through the Extern
keyword, you should not use structures.
--
Benoît Minisini "
;D
Inoltre aggiunge:
" In Gambas, a structure is a class. As soon as you use it exactly like a
class, there is no difference.
Struct MyStruct
...
End Struct
Private aStruct As MyStruct
Private aClass As MyClass
--> aClass and aStruct behave the same.
But there is something more when you use the Struct keyword in the
declaration:
' MyClass.class
Private aStruct As Struct MyStruct
Then the contents of the structure is embedded in the object where it is
declared. Consequently, to use it like another object (i.e.
MyObject.aStruct.XXX), Gambas has to create a temporary object, and so
it is slower than using a real object.
There is the same distinction with array of structures:
' MyClass.class
Private arrayOfStruct[XXX] As Struct MyStruct
arrayOfstruct[1] is slower than if the declaration has been:
Private arrayOfStruct As New MyStruct[XXX]
(note that in the late case, you have to create instances of MyStruct to
populate the array).
I hope it's a bit clearer now.
Regards,
--
Benoît Minisini "
Mah.... guarda.... la sostanza sta tutta qua:
" In other words, unless you talk to a C shared-library through the Extern
keyword, you should not use structures. "
In effetti, come ha ben intuito sotema, Minisini (ma anche altri, come ho riportato) ritiene sia più opportuno utilizzare le risorse più propriamente native del linguaggio Gambas.
Lui non può, però, negare la concessione di un uso delle Strutture (più proprie del C ) nei casi di gestione di librerie esterne. Infatti - come in modo un po' forte ti risposi in questa discussione - passare a funzioni esterne scritte in C una risorsa come un array di tipo Variant, significa non conoscere i sorgenti (....scritti in C :D fra l'altro !) del linguaggio Gambas.
La Struttura del linguaggio C, in fondo, è un elemento semplice, un'area di memoria riservata, nella quale si allineano uno dopo l'altro vari membri in base al loro tipo di dato. Il problema gigantesco (questo sì che è un "problema" di Gambas) è che Gambas non riesce - o meglio, non sempre, non in tutti i casi riesce - a gestire precisamente le Strutture passate o da passare ad una funzione esterna scritta in C. Questa è una delle ragioni principali per la quale Tobias Boege ritiene opportuno creare in tali casi un apposito Componente di Gambas scritto in C.... io però appartengo ad un'altra scuola di pensiero, secondo la quale devi essere tu programmatore in questi casi difficili a conciliare in qualche modo tutto e a calcolare gli esatti allineamenti e compensazioni.
Su questo argomento ho abbozzato questi due mattoni micidiali: (http://forum.ubuntu-it.org/images/smilies/stars.gif)
http://www.gambas-it.org/wiki/index.php?title=Gestire_con_sicurezza_le_Strutture_esterne
http://www.gambas-it.org/wiki/index.php?title=Gestire_con_un_Puntatore_le_Strutture_esterne
In Gambas il suo Variant è definito da un paio di Strutture:
/* Gambas VARIANT datatype definition */
typedef
struct {
GB_TYPE type;
union {
char _boolean;
unsigned char _byte;
short _short;
int _integer;
int64_t _long;
float _single;
double _float;
GB_DATE_VALUE _date;
char *_string;
intptr_t _pointer;
void *_object;
int64_t data;
}
value;
}
PACKED
GB_VARIANT_VALUE;
typedef
struct {
GB_TYPE type;
GB_VARIANT_VALUE value;
#if __WORDSIZE == 64
int64_t _pad;
#endif
}
GB_VARIANT;
comprensibili solo da gambas.