Newsgroup: it.comp.lang.c++
Titolo: Splittare una stringa

Ciao a tutti,
qual è la maniera migliore per splittare (rispetto ad un carattere ad
es. lo spazio) una string in un array di string?
Ad esempio la stringa
"Ciao a tutti!"
dovrebbe essere trasformata in un array di string di lunghezza 3
contenente "Ciao", "a" e "tutti!".
Ho trovato esempi che utilizzano stringstream e Vector, ma mi chiedevo
se la classe string non avesse un metodo che permetta di splittare...

 qual � la maniera migliore per splittare (rispetto ad un carattere ad

Non credo che troverai una cosa del genere. Tipicamente le varie parti 
della libreria std cercano di essere indipendenti tra loro. Dunque puoi 
usare le std::string senza usare i std::vector e viceversa. Per questo 
non troverai una funzione sulle stringhe che restituisce un vector.
Io farei cos� (non testato):
string s="ciao mare";
vector<string> v;
for(size_t p=0;p<s.size();) {
   size_t q=s.find(' ',p);
   v.push_back(string(s,p,q-p));
   p=q;
}
E.


[...]
La soluzione con std::istringstream è probabilmente meno prona ad
errori, perché agisce ad un livello più alto.
A livello di performance, è ugualmente prestante.
Matteo

   p=q;
p = q+1 se no riparte sempre dallo stesso punto :D
Cosi funziona:
#include <iostream>
#include <string>
#include <vector>
int main() {
    std::string s = "Messaggio di prova";
    std::vector < std::string > v;
    for (int p = 0; p < s.size();) {
        int q = s.find(' ', p);
        v.push_back(s.substr(p, q - p));
        if (q == std::string::npos)
           break;
        p = q + 1;
    }
    for (int i = 0; i < v.size(); i++)
        std::cout << v[i] << std::endl;
    return 0;
}

 L'argomento qui � una string con tre parametri?
Non sapevo si potesse passare come parametro una sottostringa di una 
stringa. 

 p = q+1 se no riparte sempre dallo stesso punto :D
Giusto! Anzi, se non vuoi che due spazi consecutivi ti diano una stringa 
vuota nell'array, dovresti fare cosï¿œ:
if (q!=std::string::npos) for (p=q+1;s[p]==' ';p++);


 A livello di performance, � ugualmente prestante.
Su questo dubito. Sulla mia macchina la mia implementazione � il doppio 
pi� veloce di quella fatta con istringstream [codice in fondo al messaggio]:
 > time ./test
real    0m5.194s
user    0m5.192s
sys     0m0.000s
 > time ./test 2
real    0m2.614s
user    0m2.612s
sys     0m0.000s
E.
#include <iostream>
#include <vector>
#include <sstream>
using namespace std;
string mesg="String streams are similar to the <iostream> and <fstream> 
libraries, except that string streams allow you to perform I/O on 
strings instead of streams. The <sstream> library provides functionality 
similar to sscanf and sprintf in the standard C library.";
void split1(const string &mesg, vector<string> &r) {
   istringstream in(mesg);
   string tmp;
   while (in>>tmp) {
     r.push_back(tmp);
   }
}
void split2(const string &s, vector<string> &r) {
   for (int p = 0; p < s.size();) {
     int q = s.find(' ', p);
     r.push_back(s.substr(p, q - p));
     if (q == std::string::npos)
       break;
     p = q + 1;
   }
}
main(int argc, char *argv[]) {
   vector<string> r;
   for (int i=0;i<(1<<16);++i) {
     r.resize(0);
     if (argc==1)
       split1(mesg,r);
     else
       split2(mesg,r);
   }
   if (0) {
     for (int i=0;i<r.size();++i)
       cout<<i+1<<". "<<r[i]<<endl;
   }
}


Compilato con -O3? In più, dovresti usare "std::isspace", e
considerare il caso delle stringhe con più di uno spazio di seguito.
Inoltre, siamo sicuri che il test2 non sia più veloce perché a cache
calda?
Matteo

 calda?
Penso che le obiezioni che fai siano irrilevanti, ma se hai dubbi puoi 
fare tu stesso la prova. Io non ho interesse a perderci altro tempo.
E.

   }
Qui piuttosto che cancellare le istruzioni hai preferito racchiuderle tra un 
if sempre falso? :-D 

 if sempre falso? :-D 
Sï¿œ, perchï¿œ puï¿œ sempre tornare utile. Se vuoi verificare che l'algoritmo 
effettivamente funziona, basta che cambi un carattere per riattivare 
quel codice. La variante:
if (1) {
   ...
} else {
   ...
}
ï¿œ pure molto utile.
E.

 E.
Ti sbagli, non si possono fare benchmark senza esprimere i flag usati
per la compilazione, la versione del compilatore e delle STL, e se la
cache è calda o fredda.
Altrimenti i risultati sono falsati da eventi esterni. È scritto su
ogni buon manuale di architettura degli elaboratori, di costruzione di
compilatori, e lo vedi anche sui test che trovi su Internet. È
probabile che la differenza alla fine sia ancora largamente in favore
del tuo test 2, però almeno i dati sarebbero statisticamente
rilevanti.
Ad ogni modo, reputo la tua risposta arrogante. Se non volevi perderci
altro tempo, ti era sufficiente evitare di rispondere.
L'educazione prima di tutto.
Matteo

 codice.
Si, ma in genere io in questi casi commento e decommento :D 

 Si, ma in genere io in questi casi commento e decommento :D 
In certi casi ï¿œ meglio commentare. Ma rispetto al commento, il sistema 
di mettere il codice in un blocco if (0) ha i seguenti vantaggi:
- per attivare il codice basta modificar un solo carattere: 0->1;
- in futuro puï¿œ darsi che al posto di 0 tu ci voglia mettere una 
variabile per decidere il comportamento in runtime;
- il codice nel blocco viene comunque compilato, in questo modo hai 
maggiori garanzie che funzioni quando lo riattivi.
E.

 maggiori garanzie che funzioni quando lo riattivi.
Io per risolvere questi problemi ho sempre usato (e visto usare) #ifdef
DEBUG e simili.
La compilazione condizionale e' un metodo standard in C/C++, e' ben
supportata dai build-tool ed e' piuttosto esplicativa per la maggior
parte delle persone. Viceversa un if(cond) non ha lo stesso valore
semantico, bisogna proprio entrare, leggere, capire che effettivamente
quella e' una cosa disabilitata perche' dipende dal debug.
Un altro problema e' che spesso si vogliono avere funzioni ed
eventualmente classi disponibili solo quando si e' in debug: queste non
le puoi proteggere con degli if.
Poi fai notare che il codice e' compilato sempre: questo e' uno
*svantaggio*. Con la compilazione condizionale, se il codice e'
sbagliato, prendero' un errore di compilazione e amen. Viceversa non
sono costretto a tenere librerie di (eventualmente pesanti) nelle unita'
di traduzione per codice che viene buttato via dal compilatore.
Si ingrossano gli eseguibili parecchio, ma soprattutto si hanno tempi di
compilazione inutilmente lunghi. Il che alla lunga puo' proprio
danneggiare la qualita' media del codice. 
-- 
-riko

 *svantaggio*. 
Dipende. Nel caso di utilizzo che ti ho proposto � per� un vantaggio. 
Perch� l'idea � che quel codice, prima o poi, verr� abilitato, e che � 
disabilitato solo temporaneamente.
E.

 }
Io una cosa del genere non la scriverei: farei una classe "logger" di
cui chiamo "sempre" il metodo. Poi la classe logger puo' essere
specializzata a compile time per essere una NOP, essere
un'implementazione che a seconda di determinate configurazioni stampa,
non stampa, scrive su un determinato file, etc etc etc).
In generale non mi piace avere molti costrutti condizionali/loop
annidati e/o infilati nel flusso di esecuzione. Trovo che sia tutta roba
che "spezza" all'occhio il flusso logico di esecuzione e che di
conseguenza rende il codice meno leggibile.
Nota che le librerie di logging "moderne" sono fatte sostanzialmente
come dico io, ma anche altri esempi possono essere gestiti in modo
concettualmente analogo.
-- 
-riko

 non stampa, scrive su un determinato file, etc etc etc).
Probabilmente non mi so spiegare. Se ti v� guarda il codice completo che 
ho postato nel messaggio del 4/7, e vedi come lo avresti scritto tu. 
Scopo del codice � confrontare la velocit� di due diverse funzioni che 
eseguono la stessa operazione.
E.

 ...
Anche se in ritardo... GRAZIE a tutti, avete risolto il mio problema :o)