Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: Riccardo Ferrari - Febbraio 10, 2024, 05:54:51 pm

Titolo: Array
Inserito da: Riccardo Ferrari - Febbraio 10, 2024, 05:54:51 pm
Ecco dunque il tema che mi preme molto più del case dei nomi dei file.
Si tratta delle Array di AnsiString, ma il discorso vale per le Array di qualunque cosa.
Quando si dichiara un’Array dinamica, prima di iniziare a fare qualunque cosa devo stabilirne la dimensione.
Setlength(Array,Dword)
Come faccio a sapere PRIMA quando deve essere grande l’Array?
Infatti dopo aver elaborato i dati ed avuto la loro dimensione esatta, si rifà setlength con la dimensione giusta.
Non è possibile ovviare a ciò?
Ho un file di testo che leggo per intero, per poi elaborarlo in vario modo. Metto tutte le linee dentro un’Array. Ma quante sono le linee lo so solo dopo averlo letto. Ma devo dimensionare l’array prima di leggerlo. Per cui che fare?
1) si esagera, mettendo una setlength enorme, per poi ridurla dopo aver letto il file
2) come faccio da un po' di tempo, leggo il file di testo una prima volta solo per determinare il numero delle linee, dimensiono l’array e poi rileggo il file di testo per metterlo nell’array.
3) mettere una setlength un po’ di più del comune (ritenendo che una enorme comprometta le risorse del computer) però rischiando di andare in overflow (mi è successo)
Ringrazio quanti vorranno intervenire
Titolo: Re:Array
Inserito da: DragoRosso - Febbraio 10, 2024, 06:57:18 pm
Non puoi in alcun modo caricare un array (dinamico o no) con più dati rispetto alla sua capienza.
E non c'è modo di alimentare l'array "prima" in maniera che si autodimensioni.

Quello che si fà normalmente è leggere qualsiasi dato tramite un STREAM, poi vista la lunghezza dello stream si dimensiona l'array e vi si trasferiscono i dati.
Ci sono alcune funzioni che ritornano un array dinamico, però complesso. Un esempio sono le funzioni che ritornano una lista compatibile con TList.
Ma dietro c'è comunque una lettura e un successivo ridimensionamento "nascosto" al programmatore.

Ci sono poi componenti utili per leggere testo come TRichEdit, TMemo e simili.

P.S.: ci sono modi veloci per caricare array, sempre che non ci siano trasformazioni di dati e i tipi di dati siano esattamente IDENTICI tra i due contenitori.
Vedi la funzione MOVE: https://www.freepascal.org/docs-html/rtl/system/move.html (https://www.freepascal.org/docs-html/rtl/system/move.html), ma l'array comunque DEVE essere già dimensionato.

Tieni anche presente chein un array la memoria allocata è a blocchi, cioè ogni "riga" di un array (nel caso di array multidimensione) viene garantita allineata (cioè puoi usare il MOVE) ma tra una riga e un'altra non c'è garanzia di continuità.


Titolo: Re:Array
Inserito da: bonmario - Febbraio 10, 2024, 08:02:18 pm
Ho un file di testo che leggo per intero, per poi elaborarlo in vario modo. Metto tutte le linee dentro un’Array. Ma quante sono le linee lo so solo dopo averlo letto. Ma devo dimensionare l’array prima di leggerlo. Per cui che fare?
1) si esagera, mettendo una setlength enorme, per poi ridurla dopo aver letto il file
2) come faccio da un po' di tempo, leggo il file di testo una prima volta solo per determinare il numero delle linee, dimensiono l’array e poi rileggo il file di testo per metterlo nell’array.
3) mettere una setlength un po’ di più del comune (ritenendo che una enorme comprometta le risorse del computer) però rischiando di andare in overflow (mi è successo)

Ciao,
non sono sicuro di aver capito la domanda, comunque, se è come ho capito, non devi per forza dimensionare l'array a priori, puoi aumentare di volta in volta la sua dimensione.

Qui un esempio in cui, leggendo un file di testo, andavo a salvarmi i dati in un array:

Codice: [Seleziona]
procedure TForm1.VerificaCsv(NomeCsv: String);
type TTipoPosizDate = array of Integer;
const ChrSost='^';
      ChrDaCerc=#0;
var St, StOrig, StrTestata:String;
    f:TextFile;
    ListaWrk:TStringList;
    PosizDate:TTipoPosizDate;
    VFPrimaRiga, VFFileDaElaborare, VFDataOk, VFPrimoErrore:Boolean;
    Idx, IdxArray:Integer;
begin
  //Inizializzazione variabili di lavoro
  VFPrimoErrore:=True;
  VFPrimaRiga:=True;
  VFFileDaElaborare:=True;

  AssignFile(f, UTF8ToSys(NomeCsv));
  FileMode:=CstApriFileSolaLett;
  try
    Reset(f);

    while (not Eof(f)) and
          VFFileDaElaborare do begin
      Readln(f, St);
      StOrig:=St;

      //Scompongo la stringa nei suoi elementi principali
      ListaWrk:=TStringList.Create;
      try
        ScomponiStringa(';', St, ListaWrk, True, False);

        //Se è la prima riga, valorizzo l'array in cui mi salvo le posizioni dei campi
        //di tipo data nel CSV
        if VFPrimaRiga then begin
          VFPrimaRiga:=False;

          StrTestata:=StOrig;

          PosizDate:=nil;
          IdxArray:=0;
          for Idx:=0 to ListaWrk.Count - 1 do begin
            St:=UpperCase(ListaWrk[Idx]);
            System.Delete(St, 1, Pos('_', St));
            if (Copy(St, 1, 4) = 'DATA') then begin
              SetLength(PosizDate, IdxArray + 1);
              PosizDate[IdxArray]:=Idx;
              Inc(IdxArray);
            end;
          end;

          //Se il file non ha nemmeno una data, è inutile proseguire !!!
          if (IdxArray = 0) then begin
            VFFileDaElaborare:=False;
          end;
        end else begin
          VFDataOk:=True;
          for Idx:=Low(PosizDate) to High(PosizDate) do begin
            if VFDataOk then begin
              if (not DataOk(ListaWrk[PosizDate[Idx]])) then begin
                VFDataOk:=False;
              end;
            end;
          end;

          //Se almeno una delle date non è corretta, lo segnalo
          if (not VFDataOk) then begin
            if VFPrimoErrore then begin
              VFPrimoErrore:=False;
              Memo1.Lines.Add('');
              Memo1.Lines.Add(NomeCsv);
              Memo1.Lines.Add(StrTestata);
            end;

            //Se il file contiene caratteri #0, lo segnalo
            if (Pos(ChrDaCerc, StOrig) > 0) then begin
              StOrig:=StringReplace(StOrig, ChrDaCerc, ChrSost, [rfReplaceAll]);
              Memo1.Lines.Add('    ==> La riga seguente conteneva caratteri #0, sostituiti con ' + ChrSost);
            end;
            Memo1.Lines.Add('  '+ StOrig);
          end;
        end;
      finally
        PulisciLista(ListaWrk, tpFree);
      end;
    end;
  finally
    CloseFile(f);
  end;
end;


In particolare, in questo pezzettino qui, all'inizio l'array è a nil, e la sua dimensione è 0.
Poi, per ogni dato buono che trovo, aumento di 1 la dimensione

Codice: [Seleziona]
          PosizDate:=nil;
          IdxArray:=0;
          for Idx:=0 to ListaWrk.Count - 1 do begin
            St:=UpperCase(ListaWrk[Idx]);
            System.Delete(St, 1, Pos('_', St));
            if (Copy(St, 1, 4) = 'DATA') then begin
              SetLength(PosizDate, IdxArray + 1);
              PosizDate[IdxArray]:=Idx;
              Inc(IdxArray);
            end;
          end;

Ciao, Mario
Titolo: Re:Array
Inserito da: quack - Febbraio 10, 2024, 08:50:45 pm
Quando si tratta di file di testo solitamente uso le TStringList. Leggere e salvare è un attimo.
Certamente si può usare anche l'array dinamico, in ogni caso prima di scegliere tra array o lista bisogna
capire quale si adatta meglio alla tua situazione.

La tua era una domanda generica o hai un caso di esempio su cui devi lavorare ?
Se ci spieghi cosa devi fare è più facile risponderti.

Ciao
Titolo: Re:Array
Inserito da: Riccardo Ferrari - Febbraio 10, 2024, 09:31:47 pm
Ringrazio quanti hanno risposto.
Elaboro le vostre indicazioni.