PDA

View Full Version : [C++/STL]Ricerca


Luc@s
21-12-2003, 12:29
Mi sto facendo una mia lib comene x tutti i miei programmi, che sia il + possibili portabile.
Sto facendo la generica classe CParse per caricare files di conf.
Questa sara la base x CCOnfigParse.

// Parse.hpp
#ifndef PARSE_HPP_
#define PARSE_HPP_
#include "Define.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstring>

using namespace std;

N_BEG
/**
<b>Name</b>:<i>CParse</i> \n
<b>Author</b>:<i>Kleidemos</i> \n
<b>Description</b>:<i>Parserizza una configurazione generica</i> \n
Indicata come base per sviluppi \n
*/
class CParse
{
friend CParse& operator+=(CParse &p, string _add);
public:
CParse() { _buf = " "; };
CParse(string buf){ _buf = buf; };
CParse(char * buf){ _buf = buf; };
~CParse();
/// Read a generic config
void Read(istream &is = cin, string * buf_r = "");
/// Write a generic config
void Write(ostream &os = cout, string buf = " ");
/// Add a text
inline void Add(string add) { _buf += add; };
/// Find a info
string Find(string info);
private:
string _buf;
};
N_END

#endif


Questo è la sua dichiarazione.
Ho implementato +=, Write, Add.
Ma nn so come implementare Find.
Questo metodo dovrebbe cercarmi nel buffer caricato con Read(ancora da sistemare) un determinato valore(tipo file ini opt=valore).
Ma nn so come implementarla:(
Al momento ho implementato solo un metodo per sapere se è un files di config o no.

/**
<b>Name</b>:Find\n
<b>Return</b>L'informazione trovata\n
<b>Argoments</b>:L' informazione da cercare\n
<b>Description</b>:Find a info\n
*/
string CParse::Find(string info)
{
const string CONFIG_MARK = "[CONFIG]";
if(_buf.substr(0, CONFIG_MARK.size()) == CONFIG_MARK){
/// TODO
string::size_type val;
val = _buf.find(info, 0 );
if(val != string::npos){// trovata
string ret;

}else{// non trovata
return "Error!\n";
}

}else{
return "Error!\n";
}
}

cionci
21-12-2003, 17:47
Scusa, ma se carichi già il file in memoria non ti conviene già caricarlo sotto forma di Sezione, Voce, Valore ?

Luc@s
22-12-2003, 09:00
string CParse::Find(string info)
{
const string CONFIG_MARK = "[CONFIG]";
if(_buf.substr(0, CONFIG_MARK.size()) == CONFIG_MARK)
{
/// TODO
string::size_type val;
val = _buf.find(info, 0 );
if(val != string::npos){// trovata
string ret;
string::size_type equals = pos+1;
if(info.at(equals) == "=")
{
string::size_type fin = _buf.find(";", 0 );// fine dell'info
if(fin != string::npos)
{
ret.assign(equals+1, fin);
}else{
ret = "Error!";
}
}

}else{// non trovata
ret = "Error!";
}

}else{
ret = "Error!";
}
return ret;
}


Una cosa simile?

Pero nn mi va:(

cionci
22-12-2003, 10:57
Originariamente inviato da Luc@s
Pero nn mi va:(
Ripeto: carica prima i dati sequenzialmente in un vector di struct che contengono sezione, nome e valore (sarebbe carino anche un map di map, in questo modo avresti accesso alleinformazioni direttamente con i nomi della sezione e del nome della voce, comunque parti rpima dal vector di struct)...

fek
22-12-2003, 11:26
Segui il consiglio di cionci. Mentre leggi il file, organizzalo gia' in sezioni e chiavi, fai un parsing e poi la ricerca diviene banale.

Un paio di noticine sul codice:

Originariamente inviato da Luc@s

friend CParse& operator+=(CParse &p, string _add);


Non usare un operatore in questo caso. Anche se sembra piu' elegante poter scrivere:

parse += std::string("una stringa");

Questa soluzione e' meno leggibile dell'equivalente:

parse.add(std::string("una stringa"));

In generale evita di usare metodi operatore e se proprio devi usarli, disegna il metodo di modo che si comporti in maniera naturale, in altre parole "fai come farebbero gli interi".

Ad esempio:
a += b
si deve comportare come:
a = a + b

perche' e' cio' che un qualunque programmatore si aspetterebbe leggendo il tuo codice. Ricordati che devi sempre programmare tenendo presente che qualcuno potra' leggere il tuo codice e deve essere in grado di capirlo il piu' velocemente possibile.

Un'altra nota, invece di passare il parametro _add come stringa per valore, passalo come const reference:
friend CParse& operator+=(CParse &p, string const& _add);

Altrimenti ti tocchera' pagare con l'inutile costruzione e distruzione di un oggetto temporaneo ogni volta che invochi il metodo. E la costruzione e distruzione di una stringa sono operazioni piuttosto costose. Occhio quando usi STL perche' se non sai esattamente quello che stai facendo, tende a diventare mooooooooolto lenta.

verloc
23-12-2003, 08:40
Come giustamente fatto rilevare da Cionci il problema è mal-impostato.

Devo avvertirti che l'errrore concettuale che credo tu faccia è nell'adoperare gli algoritmi standard (find):

nel 99% dei casi non restituiscono una posizione ma un iteratore (cosa assai diversa).

inoltre la std string ha anche i suoi algoritmi speciali che spesso sostituiscono quelli standard e che vengono implementati come
metodi propri.

Non so se sono stato chiaro :)

Luc@s
30-12-2003, 09:50
Risolto:
// Parse.hpp
#ifndef PARSE_HPP_
#define PARSE_HPP_

#include "Define.hpp"

#include <cstdio>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <cstring>

using namespace std;

N_BEG

/**
<b>Name</b>:<i>SInfo</i> \n
<b>Author</b>:<i>Kleidemos</i> \n
<b>Description</b>:<i>Configurazione</i> \n
*/
typedef struct SInfo
{
string key;
string value;
} Info;


/**
<b>Name</b>:<i>CParse</i> \n
<b>Author</b>:<i>Kleidemos</i> \n
<b>Description</b>:<i>Parser classs for generic text</i> \n
*/
class CParse
{
public:
CParse(){ _filename = "prog.conf"; };
CParse(string name){ _filename = name; };
CParse(char * name){ _filename = name; };
~CParse(){};
virtual void Read(char separator = 'v');
virtual void Write(char separator = 'v', string key = "", string value = "");
void Test();
virtual string Find(string key);
private:
string _filename;
string _buf;
vector<Info> conf;
};

N_END

#endif

// Parse.cpp

#include "Parse.hpp"

N_BEG
/**
<b>Name</b>:Read\n
<b>Return</b>: -\n
<b>Argoments</b>: A separator [ DEFAULT: v ] \n
<b>Description</b>:Puts a generic config into internal buffer\n
*/
void CParse::Read(char separator)
{
ifstream IN(_filename.c_str());
char code;
string key, value;
Info inf;
while(IN)
{
IN >> code;
if(code == separator) // a key find
{

IN >> key >> value;
inf.value = value;
inf.key = key;
conf.push_back(inf);
}
else
{
key = " ";
value = " ";
// conf.push_back(inf);
}
}
IN.close();
}
/**
<b>Name</b>:Write\n
<b>Return</b>:-\n
<b>Argoments</b>:
- The name of options
- The value\n
- A separator [ DEFAULT: v ]

<b>Description</b>:Write a generic config value
*/
void CParse::Write(char separator, string key, string value)
{
ofstream OUT(_filename.c_str(), ios::app);
OUT << separator << " " << key << " " << value << "\n";
OUT.close();
}
/**
<b>Name</b>:Find\n
<b>Return</b>:A Value of option\n
<b>Argoments</b>: The name of option \n
<b>Description</b>: Findo a option\n
*/
string CParse::Find(string key)
{
string ret;
vector<Info>::iterator It = conf.begin();
for(; It != conf.end();++It)
{
if(It->key == key)
ret = It->value;
}
return ret;
}
/**
<b>Name</b>:Test\n
<b>Return</b>:-\n
<b>Argoments</b>: -
<b>Description</b>: Test the class\n
<b>ONLY FOR DEBUG</b>
*/
void CParse::Test()
{
// Inserting
Write('v', "prova", "si"); // write prova
Write('v', "provas", "no");// write provas
Write('v', "Schermo", "1024x768");// write Schermo
Write('n', "Schermos", "1024x768");// write error value
// Reading
Read('v'); // puts read file
// Printing
typedef vector<Info>::iterator It;
It it = conf.begin();
for(;it != conf.end();++it) // iterator
{
cout << it->key << " => " << it->value << "\n";
}
// Finding
cout << "Finding prova => " << Find("prova") << "\n";
cout << "Finding provas => " << Find("provas") << "\n";
cout << "Finding Schermo => " << Find("Schermo") << "\n";
cout << "Finding Schermos => " << Find("Schermos") << "\n";// try to find error value

}

N_END

cionci
30-12-2003, 10:03
Sì..ora va bene...

Luc@s
30-12-2003, 10:05
Originariamente inviato da cionci
Sì..ora va bene...

davvero?
E migliorabile???


P.S: è portabile???