* * * *

Privacy Policy

Blog italiano

Clicca qui se vuoi andare al blog italiano su Lazarus e il pascal.

Forum ufficiale

Se non siete riusciti a reperire l'informazione che cercavate nei nostri articoli o sul nostro forum vi consiglio di visitare il
Forum ufficiale di Lazarus in lingua inglese.

Lazarus 1.0

Trascinare un file nel programma
DB concetti fondamentali e ZeosLib
Recuperare codice HTML da pagina web
Mandare mail con Lazarus
Stabilire il sistema operativo
Esempio lista in pascal
File INI
Codice di attivazione
Realizzare programmi multilingua
Lavorare con le directory
Utilizzare Unità esterne
TTreeView
TTreeview e Menu
Generare controlli RUN-TIME
LazReport, PDF ed immagini
Intercettare tasti premuti
Ampliare Lazarus
Lazarus e la crittografia
System Tray con Lazarus
UIB: Unified Interbase
Il file: questo sconosciuto
Conferma di chiusura di un applicazione
Liste e puntatori
Overload di funzioni
Funzioni a parametri variabili
Proprietà
Conversione numerica
TImage su Form e Panel
Indy gestiore server FTP lato Client
PopUpMenu sotto Pulsante (TSpeedButton)
Direttiva $macro
Toolbar
Evidenziare voci TreeView
Visualizzare un file Html esterno
StatusBar - aggirare l'errore variabile duplicata
Da DataSource a Excel
Le permutazioni
Brute force
Indy 10 - Invio email con allegati
La gestione degli errori in Lazarus
Pascal Script
Linux + Zeos + Firebird
Dataset virtuale
Overload di operatori
Lavorare con file in formato JSON con Lazarus
Zeos ... dietro le quinte (prima parte)
Disporre le finestre in un blocco unico (come Delphi)
Aspetto retrò (Cmd Line)
Lazarus 1.0
Come interfacciare periferica twain
Ubuntu - aggiornare free pascal e lazarus
fpcup: installazioni parallele di lazarus e fpc
Free Pascal e Lazarus sul Raspberry Pi
Cifratura: breve guida all'uso dell'algoritmo BlowFish con lazarus e free pascal.
Creare un server multithread
guida all'installazione di fpc trunk da subversion in linux gentoo
Indice
DB concetti fondamentali e connessioni standard
Advanced Record Syntax
DB concetti fondamentali e DBGrid
DB concetti fondamentali e TDBEdit, TDBMemo e TDBText
Advanced Record Syntax: un esempio pratico
Superclasse form base per programmi gestionali (e non)
Superclasse form base per programmi gestionali (e non) #2 - log, exception call stack, application toolbox
Superclasse form base per programmi gestionali (e non) #3 - traduzione delle form
Superclasse form base per programmi gestionali (e non) #4 - wait animation
Un dialog per la connessione al database:TfmSimpleDbConnectionDialog
Installare lazarus su mac osx sierra
immagine docker per lavorare con lazarus e free pascal
TDD o Test-Driven Development
Benvenuto! Effettua l'accesso oppure registrati.
Novembre 24, 2024, 05:55:21 pm

Inserisci il nome utente, la password e la durata della sessione.

36 Visitatori, 0 Utenti

Autore Topic: Dynamic array e ansistring  (Letto 9105 volte)

kalagan

  • Newbie
  • *
  • Post: 7
  • Karma: +0/-0
Dynamic array e ansistring
« il: Dicembre 04, 2011, 09:39:33 am »
Ciao a tutti.
Ho un problema che mi sta facendo diventare pazzo.

Scenario.
Nel mio applicativo ho la necessità di leggere circa un centinaio di file di testo. Su questi file devo eseguire un'operazione di parsing nel senso che ogni riga contiene diverse informazioni e di queste mi interessano solo 3: un campo TDateTime, un campo stringa (standard) e un campo AnsiString. (domanda: è più efficiente usare le regular expression o usare comandi tipo extractword e ansireplacestr?)
Quindi ho definito un nuovo tipo come segue:

Codice: [Seleziona]
  TRecord = packed record
              Description:ansistring;
              DT:tdatetime;
              Component:string;
            end;
  TDataRec= array of TRecord;

Adesso, siccome ho la necessità di prendere il contenuto di tutti i file e di ordinarli per il campo TDateTime, procedo nel seguente modo:
  • ho una variabile globale Data di tipo TDataRec
  • ho una variabile temporanea Arr1 che uso per prendere i dati dalla funzione che interpreta i singoli file, sempre di tipo TDataRec
  • all'interno di un ciclo che processa tutti i file, copio la zona di memoria della variabile arr1 nella variabile data
  • nel caso in cui il file che ho letto non è conforme allo standard, non "appendo" il suo contenuto all'array e lo elimino dall'elenco dei file da processare
e quì si verifica il problema (in Lazarus, uso l'opzione -gh) perchè, se non procedo con l'operazione di copia della memoria, non riesco a liberare la memoria della variabile Arr1.

Vi posto il codice che uso.
Codice: [Seleziona]
procedure TForm1.LoadData(var List:TFileList; var Data:TDataRec);
var
  c,n:integer;
  Arr1:TDataRec;
  previous:integer;
  tmp:string;
begin
  c:= 0;
  while c < List.Count do
  if (List.Files[c].ReadStatus<>rsReaded) and (List.Files[c].FileStatus<>fsUnknown) then
  begin
    tmp:=List.Files[c].Name;
    if ShowInfoPanel then
    begin
      StaticText4.caption:=List.Files[c].Name;
      application.ProcessMessages;
    end;
    Arr1:=List.Files[c].GetData(0); //quì popolo la variabile temporanea con i dati
    if (List.Files[c].FileType=ftUnknown) and (mode=mDirectory) then //se i dati non sono conformi
    begin //butto via il file dall'elenco
      List.Delete(c);
      DeleteX(ListMonitored,c);
      UpdateLeftGrid;
    end
    else
    begin //altrimenti
      Previous:=length(Data); //salvo in previous la dimensione dell'array globale
      SetLength(Data, Previous+length(arr1)); //ingrandisco la variabile per ospitare i nuovi dati
      Move(Arr1[0], Data[Previous], Length(Arr1)*SizeOf(Arr1[0])); //copio la memoria di Arr1 in Data
      inc(c);
    end;
    for n:=0 to high(Arr1) do  //tento di pulire la ram usata...
    begin
      arr1[n].Component:='';
      arr1[n].Description:='';
    end;
//    FillChar(Arr1[0],length(Arr1)*SizeOf(Trecord),#0); //altro tentativo
    SetLength(Arr1,0);
  end
  else
    inc(c);
end;

Mi spego meglio: se List.Files[c].FileType=ftUnknown non eseguo il codice dopo else e mi si verifica un leak di memoria; se List.Files[c].FileType<>ftUnknown, funziona tutto correttamente.

Presumo che il problema sia dovuto al fatto che le variabili ansistring usano il reference count e che se non deallocate correttamente i dati restano in ram.
C'è qualche anima pia che mi spiega come faccio a pulire correttamente la variabile Arr1? E' meglio evitare di usare le ansistring in favore di qualche altro tipo (pchar o similari)?

Grazie

xinyiman

  • Administrator
  • Hero Member
  • *****
  • Post: 3276
  • Karma: +12/-0
Re:Dynamic array e ansistring
« Risposta #1 il: Dicembre 04, 2011, 10:32:47 am »
Ciao, solo alcune domande.

1. Tu esattamente cosa devi fare?
2. I dati che leggi al posto di tenerli in memoria non conviene caricarli in un DB?
3. I campi della singola riga sono divisi tra di loro da qualcosa? Tipo ; e " ?

Fammi sapere
Ieri è passato, domani è futuro, oggi è un dono...

kalagan

  • Newbie
  • *
  • Post: 7
  • Karma: +0/-0
Re:Dynamic array e ansistring
« Risposta #2 il: Dicembre 04, 2011, 11:48:56 am »
Ciao xinyiman,
cerco di spiegarmi meglio che posso...

Sto realizzando un viewer di file multiplo. In pratica un servizio Microsoft ha diversi thread. Ogni thread scrive un log, indipendente l'uno dall'altro.
Il problema è che se una persona deve analizzare i log per capire cosa sta succedendo, risulta molto difficile allineare le varie informazioni loggate in ordine temporale.

Allora la mia idea è quello di caricare tutti i log in ram e ordinare l'array contenente i dati per campo data/ora.
Ti posto uno screenshot dell'applicativo...

Siccome questi log vengono scritti sia sul server che per ogni client dov'è installato questo servizio, mi serviva di realizzare un applicativo che non carica i dati in un DB specifico... ma di caricarli al runtime (sulla macchina dove viene eseguito l'applicativo). Certo potrei usare un TBufDataSet (o similari) e agganciarci una dbgrid... ma non mi è chiaro poi come gestire la visualizzazione dei dati. Infatti, primo non saprei come ordinare i dati (in modo efficiente e "stabile"), secondo non saprei come "caricare" solo i dati da visualizzare nella griglia e terzo, non saprei come, a fronte di un evento di modifica, aggiorare i dati visualizzati in tempo reale (funzione tail).
Approfondisco il tema: inizialmente ho pensato di usare una stringgrid; poi mi sono accorto che scrivere i dati (70MB) di file in una stringgrid raggiungevo i 600MB in ram; allora ho pensato (mi hanno suggerito) di usare una drawgrid, dove i dati non venivano scritti nella griglia, ma venivano "disegnati" sulla griglia. Per esempio: se devo visualizzare le righe da 17000 a 17036 (37 sarebbero le righe visualizzabili in questo esempio) allora basta "disegnare" i dati presenti nella mia array da 17000-1 a 17036-1. Così facendo l'utente può scrollare su e giù la griglia, ma i dati da "disegnare" sarebbero sempre e solo quanti sono le righe da visualizzare sullo schermo. Oltre a questo, il mio problema è che le righe da visualizzare non hanno altezza fissa: nel senso che ci sono record che contengono più righe (per cui devo modificare l'altezza della grigia). Insomma, la faccio breve, ho deciso che il componente migliore non è la drawgrid, ma una KGrid (componente esterno) dove ho dichiarato che i dati sono virtuali. E funziona tutto correttmente.
Se poi mi riesci a dare una qualche indicazione di come usare un DB per fare quanto ho bisogno (magari con un piccolo esempio) te ne sarei molto grato.

Riguardo ai dati da caricare.
Quì è un vero casino (scusa per il termine). Infatti Microsoft ha usato diversi "template", se così si possono definire. Fondamentalmente li posso dividere in 2: quelli client e quelli server.

Quelli client sono così formattati:
<![LOG[descrizione]LOG]!><time="hh:nn:ss.zzz+-timezone" date="mm-dd-yyyy" component="componente" context="" type="codice tipo" thread="thread id" file="file cpp:riga">.
Non sembra difficile prendere i dati che mi interessano... purtroppo, in realtà, il campo descrizione può essere spezzettato su più righe... che non sono terminate con il classico #13#10 ma solo con #10. E questo mi ha causato grossi problemi perchè in FPC, una riga può terminare indifferentemente con #13, #10 oppure #13#10...
ESEMPIO:
<![LOG[Raising event:

instance of CCM_PolicyAgent_SettingsEvaluationComplete
{
   ClientID = "GUID:E0DACB52-0EB8-4CA1-9E2F-755B461198F3";
   DateTime = "20110823134223.458000+000";
   PolicyNamespace = "\\\\office\\root\\ccm\\policy\\machine\\actualconfig";
   ProcessID = 2768;
   ThreadID = 916;
};
]LOG]!><time="15:42:23.458+-120" date="08-23-2011" component="PolicyAgent_PolicyEvaluator" context="" type="1" thread="916" file="event.cpp:525">

In questo caso, come faccio a capire dove finisce il record se la lettura (readln) mi termina a "<![LOG[Raising event:"??
Quindi ho dovuto riscrivermi il readln per considerare #13#10 come unico terminatore di linea possibile...

Riguardo i log server questi sono formattati così:
Descrizione  $$<componente><ddd mmm dd hh:nn:ss.zzz yyyy W. Europe Daylight Time><thread=thread id>
ESEMPIO
INFO: successfully completed directory search~  $$<SMS_AD_SYSTEM_GROUP_DISCOVERY_AGENT><Fri Aug 26 19:06:17.797 2011 W. Europe Daylight Time><thread=7924 (0x1EF4)>

Ovviamente, come nessuno avrebbe mai immaginato, "ddd mmm" possono essere indifferentemente in italiano o inglese... anche nello stesso file...(?!? come ci siano riusciti è un mistero).

xinyiman

  • Administrator
  • Hero Member
  • *****
  • Post: 3276
  • Karma: +12/-0
Re:Dynamic array e ansistring
« Risposta #3 il: Dicembre 04, 2011, 04:53:44 pm »
Mi posti un paio di righe di quei log che vedo cosa posso fare per crearti un esempio!
Ieri è passato, domani è futuro, oggi è un dono...

kalagan

  • Newbie
  • *
  • Post: 7
  • Karma: +0/-0
Re:Dynamic array e ansistring
« Risposta #4 il: Dicembre 04, 2011, 08:57:01 pm »
Ecco 2 log, uno per tipologia

xinyiman

  • Administrator
  • Hero Member
  • *****
  • Post: 3276
  • Karma: +12/-0
Re:Dynamic array e ansistring
« Risposta #5 il: Dicembre 05, 2011, 11:05:39 am »
Prova il mio allegato e fammi sapere. Per provarlo basta che prendi i due file che mi hai allegato, li metti nella cartella dove c'è il progetto e compili. E' solo un esempio per farti vedere come farei io. Poi gestiscitelo tu, metti le colonne come vuoi e via discorrendo. Se devi inserire più file inseriscili in una matrice e poi popola la griglia con quella matrice. Ciao
Ieri è passato, domani è futuro, oggi è un dono...

kalagan

  • Newbie
  • *
  • Post: 7
  • Karma: +0/-0
Re:Dynamic array e ansistring
« Risposta #6 il: Dicembre 05, 2011, 06:02:32 pm »
Ciao xinyiman,
grazie per il tuo esempio.
In realtà non è molto diverso da come procedo io...
La differenza sostanziale è che tu usi le stringlist per leggere i file e, una volta finito di fare il parsing e di inserire i dati nella stringgrid, liberi le risorse con il free.
Io invece uso le array per tenere i dati per quel discorso che ti dicevo: quando hai una cosa come 400 mila / 1 milione di righe, la stringgrid mangia un botto di ram e non è molto efficiente. Per cui se invece di usare la stringgrid usi una drawgrid, in ram hai solo i dati (dell'array) + la memoria necessaria per creare la drawgrid (grande quanto il numero di record nell'array+1, anche se con una scrollbar separata non sarebbe nemmeno necessario averla così grande) e poi disegni solo i dati che al momento sono visibili (poche righe, quanto stanno nel monitor dell'utente), andando gestire il tutto nell'evento ondrawcell. Almeno così è come l'ho pensata... e funziona molto velocemente.
Il motivo, in realtà, per cui ho chiesto aiuto, invece, è che non mi riesce di liberare la ram occupata dall'array (temporanea) Arr1, mannaggia. Ma, forse, dagli innumerevoli test che ho fatto il problema è altrove. Mi spiego meglio che posso.
Lo so che non hai bisogno di lezioni, figuriamoci da me, ma forse potrebbe essere interessante per qualcuno che legge questi post. Quando in FPC usiamo la direttiva {h+} o dichiariamo le variabili come ansistring, il compilatore abilita il reference count.
Il che significa che se scrivo il seguente codice:
Codice: [Seleziona]
var
  a,b:ansistring;
begin
  a:='questa è una'+#13#10+'ansistring';
  b:=a;
end;
B non è uguale ad A, ma B punta alla zona di memoria occupata da A e in A viene incrementato il "reference count" che dice "attenzione, c'è una variabile che fa riferimento a questa zona di memoria". Solo eseguendo una modifica in B, avviene l'effettiva copia di dei dati di A in B. Questo per risparmiare ram e aumentare la velocità.

Ma cosa succede (e questa è una domanda...) se scrivo il seguente codice?
Codice: [Seleziona]
var
  a:ansistring;

function funzione_di_test:ansistring;
var
  tmp:ansistring;
begin
  tmp:='questa è una'+#13#10+'ansistring';
  result:=tmp;
end;

begin
  a:=funzione_di_test;
end;

Quì le cose si complicano, e non poco... se RESULT punta a TMP... quando la funzione termina, TMP non può essere distrutto  in automatico perchè ha il reference count <> 0... E poi anche A punta RESULT... Quindi internamente non so come funzioni... Ma fin quì penso che il compilatore riesce a gestire il tutto, egregiamente.
Tuttavia, potrebbe essere che se invece di dichiarare le variabili di tipi semplici, vengono usati i tipi composti, la cosa potrebbe non essere così scontata. Lo stesso esempio con il tipo di dato che uso io diventa così:

Codice: [Seleziona]
{H-}
type
  TRecord = packed record
              Description:ansistring;
              DT:tdatetime;
              Component:string;
            end;
  TDataRec= array of TRecord; 
var
  a:TDataRec;

function funzione_di_test:TDataRec;
var
  tmp:TRecord;
  tmpdata:TDataRec;
begin
  tmp.description:='questa è una'+#13#10+'ansistring';
  tmp.dt:=now;
  tmp.component:='questa è una stringa standard'
  setlength(tmpdata,1);
  tmpdata[0]:=tmp
  result:=tmpdata;
end;

begin
  a:=funzione_di_test;
end;

In questo caso, nel mio codice, mi si verifica un problema di memoria (me lo dice lo heaptrc). Da qualche parte devo aver deferenziato i puntatori di una delle variabili temporanee e quindi mi restano i dati in ram...

Non so che pesci pigliare. Forse hai ragione tu: dovrei usare un DB. Sto facendo qualche esperimento con TMemDataSet (con TBufDataSet non mi riesce di usare l'AppendRecord).

xinyiman

  • Administrator
  • Hero Member
  • *****
  • Post: 3276
  • Karma: +12/-0
Re:Dynamic array e ansistring
« Risposta #7 il: Dicembre 06, 2011, 09:52:35 am »
Domanda stupida, io non ho a disposizione tutto il tuo sorgente per capire bene cosa fai o cosa non fai. Ma se scrivi nome della variabile.free o .destroy quando non ti serve più cosa succede! Generalmente la sintassi per liberare l'area di memoria occupata è NomeVariabile.Free se parliamo di una classe. Diversamente consiglio di caricarti questi dati in un DB SqlLite e poi visualizzare i dati dopo!
Ieri è passato, domani è futuro, oggi è un dono...

kalagan

  • Newbie
  • *
  • Post: 7
  • Karma: +0/-0
Re:Dynamic array e ansistring
« Risposta #8 il: Dicembre 12, 2011, 05:31:28 pm »
Ciao xinyiman,
scusa se non ti ho risposto ma ero occupato.
Volevo dirti che ci sono riuscito a liberare sta ram!!. Finalmente...

Ovviamente ero io che sbagliavo... Avevo aggiunto una riga che mi mandava in follia i puntatori delle ansistring: FillChar(rec,sizeof(TRecord),#0);. Tolta sta maledetta riga adesso la heaptrc mi riporta 0 unfreed memory blocks.
Successo informatico...

xinyiman

  • Administrator
  • Hero Member
  • *****
  • Post: 3276
  • Karma: +12/-0
Re:Dynamic array e ansistring
« Risposta #9 il: Dicembre 12, 2011, 07:25:58 pm »
Grande  ;)
Ieri è passato, domani è futuro, oggi è un dono...

 

Recenti

How To

Utenti
  • Utenti in totale: 803
  • Latest: maXim.FI
Stats
  • Post in totale: 19182
  • Topic in totale: 2288
  • Online Today: 97
  • Online Ever: 900
  • (Gennaio 21, 2020, 08:17:49 pm)
Utenti Online
Users: 0
Guests: 36
Total: 36

Disclaimer:

Questo blog non rappresenta una testata giornalistica poiché viene aggiornato senza alcuna periodicità. Non può pertanto considerarsi un prodotto editoriale ai sensi della legge n. 62/2001.