Newsgroup: it.comp.lang.c++
Titolo: Piccolo archivio

Volevo chiedervi qualche suggerimento sulla gestione di un catalogo.
Devo fare un programma che effettui la ricerca di alcuni articoli
all'interno di un catalogo scritto in formato testo (questo catalogo è già
scritto, contiene qualche centinaia di articoli e non lo posso modificare).
Mi conviene :
1) Costruire una lista su cui effettuare poi la ricerca dei prodotti.
2) Effettuare la ricerca direttamente sun file e inserire i dati riguardanti
gli articoli in un vettore.
Sono bene accetti tutti i consigli anche se non si basano su una di queste
due possibilità.
Vorrei realizzare il programma per windows98.
Grazie in anticipo.
                                                                        Bart
...


L'approccio da seguire richiede diverse considerazioni, in particolare
la dimensione del catalogo ed il modo in cui vengono effettuate le
ricerche.
Se il catalogo non e` molto grosso (come mi sembra di capire)
lo potresti caricare tutto in ram.
A questo punto la soluzione piu` semplice e` quella di organizzare
le informazioni tramite una lista ed eseguire ricerche sequenziali.
Tuttavia se devi eseguire molte ricerche, le prestazioni probabilmente
non saranno molto accettabili, specie se si effettuano ricerche
in blocco ed il programma interagisce con qualcuno.
A questo punto puoi seguire diverse strade piu` o meno complesse e con
prestazioni diverse.
Ad esempio se le ricerche sono eseguite in blocco,  potresti
ordinare il catalogo e i prodotti da cercare, in tal modo
una unica scansione del catalogo sarebbe sufficiente.
Ovviamente dipende da quante ricerche devi effettuare, dato che
l'ordinamento del catalogo ha un suo costo e per poche ricerche potrebbe
vanificare ogni vantaggio.
Se i tempi di risposta sono importanti (o se non puoi portare il file in memoria)
e se devi effettuare molte ricerche, allora e` necessario organizzare meglio
il catalogo. Ad esempio, potresti adottare una organizzazione isam:
1) ordini il catalogo e lo dividi in pagine,
2) realizzi un indice che associ al primo elemento di ogni pagina la corrispondente pagina
La ricerca di un prodotto si realizza cercando sequenzialmente nell'indice la pagina del catalogo
che lo continene, quindi si esegue una scansione sequenziale della pagina.
Paolo
----------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
  e-mail: 
mar...@cli.di.unipi.it
 
----------------------------------------------------

gli articoli in un vettore.
Se in una data esecuzione del programma fai una sola ricerca,
puo` valer la pena di farla direttamente sul file; se ne fai, almeno
in potenza, piu` di una, tanto vale succhiarti tutto il file in memoria
una volta per tutte e poi cercare in memoria, tanto pare di capire
che non sia un file gigantesco.
La struttura dati piu` appropriata in memoria puo` essere una lista,
ma, se le ricerche non sono pochissime, potrebbe anche essere
una std::map (ottenibile con #include <map> in C++ ragionevolmente
vicini allo standard --  le ultime versioni di C++Builder, MS Visual
C++, egcs, sono ad esempio tutte perfettamente sufficienti), che
e` praticamente un "array associativo" in cui usi le "chiavi" (che
sono ad esempio stringhe) come "indice".
Supponi ad esempio che il tuo "file in formato testo" abbia il
formato: 1 articolo per riga, formato della riga:
    nomearticolo tanti altri dati interessanti
cioe` "nomearticolo" come prima parola, poi il resto, con
almeno un blank di separazione.  In C++ standard puoi allora
scrivere (ti faccio l'esempio come programma "di console"
per sottolineare le strutture dati interne, ovviamente puoi
mettere questo stesso "nocciolo" dentro una GUI!-):
#include <string>
#include <iostream>
#include <fstream>
#include <map>
using namespace std;
typedef map<string,string> dict;
bool init(const char* nomefile, dict& mappa)
{
    ifstream fin(nomefile);
    if(!fin) return false;
    string nomearticolo;
    string altridati;
    while(fin) {
        fin>>nomearticolo;
        getline(fin,altridati);
        mappa[nomearticolo] = altridati;
    }
    return true;
}
int main()
{
    dict lamappa;
    if(!init("catalogo.txt",lamappa)) {
        cerr << "Non leggibile: catalogo.txt" << endl;
        return 1;
    }
    while(cin) {
        cout << "Nome articolo (Ctrl-Z per chiudere): ";
        string nomearticolo;
        getline(cin,nomearticolo);
        string datiarticolo = lamappa[nomearticolo];
        cout << nomearticolo <<": " << datiarticolo << endl;
    }
    return 0;
}
Alex

GRAZIE A ENTRAMBI PER LA VELOCITA E LA PRECISIONE DELLE RISPOSTE.
ciao
BART...

Ho provato a compilare il codice di esempio che mi hai inviato.
La compilizione da' i seguenti errori.
Warn :  string.cc(658,2):Cannot create pre-compiled header: code in header
Error:  mappa.cpp(7,28):Too few arguments in template class name 'map'
Error:  mappa.cpp(7,34):Declaration terminated incorrectly
Error:  mappa.cpp(9,38):Type name expected
Ho utilizzato il compilatore Borland C++ 5.01
Ciao
Bart...








Ho compilato il codice con EGCS-1.1 Mingw32 e funziona perfettamente.
La sintassi di map e` corretta stando alla documentazione della STL di
SGI.
Pare che il problema stia nelle librerie standard del tuo compilatore.
Prova a sostituire l'istruzione:
typedef map<string,string> dict;
con 
typedef map<string,string, less<string> > dict;
_________________________________________
Roberto Gerola
e-mail: 
roge...@tin.it
www: 
http://space.tin.it/computer/rgerola

Ho utilizzato il compilatore Borland C++ 5.01
Apparentemente, la lontananza dal C++ Standard dei
compilatori Borland colpisce ancora -- il codice che ho
postato e` standard.  Presumo, dalle diagnostiche (e
senza esaminare i sorgenti delle librerie "standard" di
Borland), che qui in particolare il problema sia che
Borland non ha un corretto trattamento degli argomenti
di default ai template; prova ad aggiungere less<string>
come terzo argomento dell'istanziazione map<string, string>
(ricordati di interporre uno spazio fra i due > di terminazione,
se no si forma il token >> che significa tutt'altra cosa) -- e
spero che un povero utente Borland non debba anche
specificare esplicitamente l'allocator da usare!
Questo dovrebbe rendere conto dei tre errori; quanto al
warning, se vuoi toglierlo, immagino che dovrai disabilitare
gli header precompilati, visto che il compilatore Borland
pare non essere in grado di farli con gli header delle
librerie standard fornite dalla Borland stessa (o se no,
puoi provare con altre versioni delle librerie standard,
come le varie, gratuite, di Cygnus, SGI, e ObjectSpace;
forse qualcuna ha una versione/configurazione che giri
con detto compilatore).
La prossima volta che qualcuno chiede consigli sulla scelta
di un compilatore, devo ricordarmi di questi problemi, quelli
sulla chiamata di virtuali in un costruttore, ecc, e dare piu`
calda raccomandazione a egcs 1.1, gratis e piu` vicino allo
standard, seguito da Microsoft, cara ma non lontana dallo
standard, e ultima Borland, che degli standard non pare che
si curi (difetto, secondo me, assai grave; se uno vuole un
RAD proprietario, allora meglio Delphi -- se compra C++,
sara`, spero, perche` NON vuole usare un linguaggio non
standard...!).
Alex

Ho provato a compilare il codice di esempio che mi hai inviato.
La compilizione da' i seguenti errori.
Warn :  string.cc(658,2):Cannot create pre-compiled header: code in header
Error:  mappa.cpp(7,28):Too few arguments in template class name 'map'
Error:  mappa.cpp(7,34):Declaration terminated incorrectly
Error:  mappa.cpp(9,38):Type name expected
Ho utilizzato il compilatore Borland C++ 5.01
Ciao
Bart...

Scusatemi forse stò sbagliando a voler utilizzare cose che non conosco
ancora bene, ma la soluzione che mi hai mandato mi piaceva però ho ancora
alcuni problemi.
I trè errori dovuti a map sono spariti ma nesono comparsi altri riguardo
getline.
Gli errori sono i seguenti:
Error:  mappa.cpp(17,31):Could not find a match for 'std::getline
(ifstream,std::basic_string<char,std::string_char_traits<char>,std::allocato
r<char>>)'
Error:  mappa.cpp(33,34):Could not find a match for 'std::getline
(istream_withassign,std::basic_string<char,std::string_char_traits<char>,std
::allocator<char>>)'
Comunque proverò a cambiare compilatore per adesso devo utilizzare questo
perchè in facoltà è l'unico disponibile.
Grazie ancora
Ciao
                                            Bart...

ancora bene, ma la soluzione che mi hai mandato mi piaceva però ho ancora
Non mi pare che tu stia sbagliando -- come imparerai
queste cose, se non provando ad usarle?  Il problema
e` nel compilatore Borland, particolarmente nelle sue
librerie, che paiono assai indietro allo standard (molto
sotto quelle che Microsoft aveva gia` in VC++ 4.2, e
persino a quelle che Gnu, prima ancora, aveva in gcc
2.7.2, pare).
Qui, ti manca, apparentemente, la template function
getline, che ha come argomenti un basic_istream<>&
e una basic_string<>& (legge una riga dal primo, e la
usa per riempire la seconda).  La diagnostica ci fa
sospettare che istream non venga visto correttamente,
infatti il messaggio dice "istream_withassign", invece di:
    std::basic_istream<char // ecc ecc
mentre, per string, porta l'espansione standard del
template.  Verifica gli header standard che Borland
ti da`; li hai inclusi correttamenti?  Molti compilatori,
per compatibilita`, hanno ad esempio un header file
non-standard <iostream.h> -- non avrai per caso
incluso quello invece di <iostream>?  Questo potrebbe
dare l'interpretazione sbagliata per cin; quella
istream_withassign risveglia lontane memorie di
tanti anni fa -- un tempo lontano, un costrutto del
genere c'era in alcune librerie C++.
Con "grep", file/find/advanced, o altri strumenti,
puoi scoprire quali "getline" offrono gli header file
a tua disposizione; puoi anche cercare questo nome
sulla doc in linea del tuo compilatore.  Se non c'e`
modo di trovare una getline che prenda una string,
potrebbe essercene una che prende un array di
caratteri, magari come membro dell'oggetto di
classe istream -- ha lo svantaggio che devi sapere
prima quanto al massimo puo` essere lunga la
riga, ma, meglio che niente; l'uso e` del tipo:
    char buf[1024];
    cin.getline(buf,1024);
    string lariga(buf);
Alex

Ho provato a compilare il codice con VC++ 5.0 e funziona.
Premesso che praticamente non sò usare questo compilatore, sono riuscito a
creare un progetto consolle in cui ho inserito il file con il programma, mi
sorge però il dubbio che ci sia da impostare qualche altra cosina perchè la
compilazione mi da' si 0 errori ma qualcosa come 99 Warning.
Vorrei chiederti un'altra cosa; se nel catalogo ci sono due prodotti con lo
stesso nomearticolo ma con datiarticolo differenti, vengono segnalati
entrambi?
Non mi pare.
Grazie ancora
Bart...
P.S.
il VC++ non è il mio ma di un amico che ne sà meno di mè :-(

Non mi pare.
No, quando scrivo "lamappa[chiave] = valore;" vengo a sovrascrivere
un eventuale valore precedentemente assegnato alla stessa chiave; e`
la stessa semantica di un array (non puoi avere due valori con lo stesso
indice).  Se hai bisogno di potere associare ad una chiave molti valori,
devi usare una std::multimap, che naturalmente NON e` cosi` comoda
da usare come una std::map (concettualmente, e` assai piu` complicata).
Alex