PDA

View Full Version : [C++] Carico una riga fantasma


jepessen
01-06-2008, 10:33
Salve a tutti.

Ho il seguente problema: ho un file di testo contenente, in ogni riga, tre numeri, ad esempio la seguente lista:

-0.01975 0.185617 -0.0153931
-0.01925 0.185622 -0.0159337
-0.01875 0.185626 -0.0164744
-0.01825 0.185633 -0.0172854
-0.01775 0.185639 -0.0179612
-0.01725 0.185647 -0.0189073
-0.01675 0.185653 -0.0195831

Ogni riga rappresenta un punto nello spazio 3D, così, per caricare i valori, mi creo la struttura point e li carico lì, assieme ad un intero che fa da ID del punto. Qua sotto c'è il codice sorgente:

#include <iostream>
#include <fstream>

int main (int argc, char** argv)
{
struct coordinates
{
double x; /**< Coordinata x */
double y; /**< Coordinata y */
double z; /**< Coordinata z */
};


/** \struct point
* La struttura contiene informazioni generali su un punto, ovvero le sue
* coordinate, un identificativo numerico e la lista dei suoi descrittori.
*/
struct point
{
long int id; /**< ID del punto */
struct coordinates coords; /**< Coordinate del punto */
struct point *next; /**< Puntatore al prossimo elemento */
};

struct point *plist, *plist_temp;
std::ifstream infile;

long int count = 0;
plist = NULL;
plist_temp = NULL;

// Apertura file
infile.open(argv[1], std::ios::in);

// Lettura dei dati nel caso il file sia aperto con successo
if (infile)
{
while (!infile.fail())
{

// Incremento del contatore
count++;

// Creazione del nuovo elemento in testa
plist_temp = plist;
plist = new struct point;

// Memorizzazione dei valori
infile >> plist->coords.x;
infile >> plist->coords.y;
infile >> plist->coords.z;

// Memorizzazione del contatore
plist->id = count;

// inserimento dell'elemento della lista
plist->next = plist_temp;

// Scrittura a schermo dei valori
std::cout << "punto n. " << count << ": " << plist->coords.x <<
" " << plist->coords.y << " " <<plist->coords.z << std::endl;


}

std::cout << "Il numero di numeri letti e' " << count << std::endl;

plist_temp = plist;
plist = plist->next;
delete plist_temp;
}
else
{
std::cout << "Errore nella lettura deil file\n";
}

infile.close();

return 0;
}


Lo compilo con g++ -Wall test.c.

Tuttavia, se lo eseguo, ottengo il seguente output:

./a.out file_con_punti
punto n. 1: -0.01975 0.185617 -0.0153931
punto n. 2: -0.01925 0.185622 -0.0159337
punto n. 3: -0.01875 0.185626 -0.0164744
punto n. 4: -0.01825 0.185633 -0.0172854
punto n. 5: -0.01775 0.185639 -0.0179612
punto n. 6: -0.01725 0.185647 -0.0189073
punto n. 7: -0.01675 0.185653 -0.0195831
punto n. 8: 0 0 0


In pratica alla fine accoda un punto in più, con coordinate tutte nulle. Ora, per il lavoro non è un grosso problema, dato che alla fine cancello il punto in più e pace fatta, ma non mi sembra un modo pulito per fare le cose.

Per sicurezza, ho provato a modificare il file contenente i punti, aggiungendo delle righe vuote, oppure senza nessuna riga vuota alla fine ma con spazi dopo l'ultima cifra dell'ultimo numero, ed infine terminando il file esattamente subito dopo l'ultima cifra dell'ultimo numero, ma il risultato non cambia, ho sempre la presenza del punto 'fantasma' che non ci dovrebbe essere.

Come posso modificare il programmino in maniera tale che non legga l'ultimo punto inesistente?

Daniele

PS: Utilizzo g++ (GCC) versione 4.2.3.

Tommo
01-06-2008, 11:15
Sicuramente il while viene eseguito una volta di troppo.

Nella condizione io metterei ( !infile.fail() && !infile.eof() ), dato che eof() è la funzione più adatta a trovare la fine del file...
magari fail() viene chiamato se si effettua una lettura dopo eof()... e in questa maniera hai comunque una lettura vuota a eof().

E poi io sarei partito da 0 nella numerazione, mettendo count++ alla fine. Però è questione di gusti :D

jepessen
01-06-2008, 11:58
Provato, ma non funge. Legge sempre la riga vuota. Anch'io pensavo al ciclo while di troppo, ma ancora non ho trovato soluzione...

Daniele

PS: Anch'io partirei da 0 per il conteggio, ma poi devo elaborare e salvare in un formato dove l'ID parte da 1, quindi mi adeguo.

marco.r
01-06-2008, 13:02
il problema e' che finche' il test iniziale ti da il risultato dell'ultima lettura, non quella di quella che stai per fare. Questo e' in particolare vero se hai un linea vuota alla fine: finche' non provi a leggere un nuovo valore il codice non si accorge che sei arrivato alla fine.
La soluzione corretta e' provare a leggere i tre valori, e _dopo_ verificare se c'e' stato un errore in lettura e in tal caso interrompere il loop, senza ovviamente salvare l'ultima lettura.

potresti sostituire il seguente codice

while (!infile.fail())
{

// Incremento del contatore
count++;

// Creazione del nuovo elemento in testa
plist_temp = plist;
plist = new struct point;

// Memorizzazione dei valori
infile >> plist->coords.x;
infile >> plist->coords.y;
infile >> plist->coords.z;

con qualcosa del tipo

double x,y,z;
while ( infile >> x >> y >> z )
{
count++;
plist_temp = plist;
plist = new struct point;

// Memorizzazione dei valori
plist->coords.x = x;
plist->coords.y = y;
plist->coords.z = z;
/* ... */

jepessen
01-06-2008, 13:18
Grazie, hai risolto il mio problema... Ti devo una birrozza...

Daniele

marco.r
01-06-2008, 13:19
Suggerimento 2, togli tutti quei commenti inutili :D.
Penso sia abbastanza chiaro che "double x" e' la coordinata x e che count++ incrementa il contatore. Se a qualcuno non risultasse chiaro, spero cambi mestiere in breve tempo :D

Suggerimento 3. Utilizza un contenitore standard per salvare i tuoi punti. A seconda dell'uso, un vector<> o una list<> dovrebbero fare al caso tuo. Anche perche' nel caso sopra mescoli informazioni relative al contenitore in se(il puntatore a next) con informazioni relative al punto (il suo id)
Meglio ancora se aggiungi i costruttori alle strutture
Ritengo piu' sensato qualcosa del genere:



struct coordinates
{
coordinates( double _x, double _y, double _z):x(_x),y(_y),z(_z){}
double x; /**< Coordinata x */
double y; /**< Coordinata y */
double z; /**< Coordinata z */
};

struct point
{
point( double x, double y, double x,long _id):id(_id),coords(x,y,z){}
long int id; /**< ID del punto */
struct coordinates coords; /**< Coordinate del punto */
};

std::list<point> pointList;

/* ... */

if (infile)
{
double x,y,z;
while ( infile >> x >> y >> z )
{
pointList.push_back( point(x,y,z, pointList.size() + 1) );
const point& p = pointList.back();
std::cout << "punto n. " << pointList.size() << ": " << p.coords.x <<
" " << p.coords.y << " " <<p.coords.z << std::endl;


}

std::cout << "Il numero di numeri letti e' " << pointList.size() << std::endl;

}

jepessen
01-06-2008, 13:27
Ok, farò tesoro dei tuoi consigli.

Daniele