Ricerca…


Osservazioni

Il costruttore predefinito di std::istream_iterator costruisce un iteratore che rappresenta la fine del flusso. Quindi, std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(), .... significa copiare dalla posizione corrente in ifs fino alla fine.

Stream di stringhe

std::ostringstream è una classe i cui oggetti hanno l'aspetto di un flusso di output (ovvero, è possibile scrivere su di essi tramite l' operator<< ), ma in realtà memorizzano i risultati di scrittura e li forniscono sotto forma di flusso.

Considera il seguente codice breve:

#include <sstream>
#include <string>                                                                                                                          

using namespace std;

int main()
{
    ostringstream ss;
    ss << "the answer to everything is " << 42;
    const string result = ss.str(); 
}   

La linea

ostringstream ss;

crea un tale oggetto. Questo oggetto viene prima manipolato come un normale flusso:

ss << "the answer to everything is " << 42;

In seguito, tuttavia, lo stream risultante può essere ottenuto in questo modo:

const string result = ss.str();

(il result della stringa sarà uguale a "the answer to everything is 42" ).

Ciò è utile soprattutto quando abbiamo una classe per la quale è stata definita la serializzazione del flusso e per la quale vogliamo una stringa. Ad esempio, supponiamo di avere qualche classe

class foo 
{   
    // All sort of stuff here.
};  

ostream &operator<<(ostream &os, const foo &f);

Per ottenere la rappresentazione della stringa di un oggetto foo ,

foo f;

potremmo usare

ostringstream ss; 
ss << f;
const string result = ss.str();        

Quindi il result contiene la rappresentazione della stringa dell'oggetto foo .

Leggere un file fino alla fine

Lettura di un file di testo riga per riga

Un modo corretto di leggere un file di testo riga per riga fino alla fine di solito non è chiaro dalla documentazione di ifstream . Consideriamo alcuni errori comuni fatti dai programmatori C ++ per principianti e un modo corretto di leggere il file.

Linee senza caratteri di spazi bianchi

Per semplicità, supponiamo che ogni riga del file non contenga simboli di spazi vuoti.

ifstream ha operator bool() , che restituisce true quando uno stream non ha errori ed è pronto per essere letto. Inoltre, ifstream::operator >> restituisce un riferimento al flusso stesso, così possiamo leggere e controllare EOF (così come gli errori) in una riga con una sintassi molto elegante:

std::ifstream ifs("1.txt");
std::string s;
while(ifs >> s) {
    std::cout << s << std::endl;
}

Linee con caratteri di spaziatura

ifstream::operator >> legge lo stream fino a quando non si verifica un carattere di spazio ifstream::operator >> , quindi il codice sopra riportato stamperà le parole da una riga su righe separate. Per leggere tutto fino alla fine della riga, usa std::getline invece di ifstream::operator >> . getline restituisce il riferimento al thread con cui ha lavorato, quindi la stessa sintassi è disponibile:

while(std::getline(ifs, s)) {
    std::cout << s << std::endl;
}

Ovviamente, std::getline dovrebbe essere usato anche per leggere un file a riga singola fino alla fine.

Lettura di un file in un buffer in una volta

Infine, leggiamo il file dall'inizio alla fine senza fermarci a nessun carattere, inclusi gli spazi bianchi e le nuove righe. Se sappiamo che le dimensioni esatte del file o il limite superiore della lunghezza sono accettabili, possiamo ridimensionare la stringa e quindi leggere:

s.resize(100);
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
    s.begin());

Altrimenti, dobbiamo inserire ogni carattere alla fine della stringa, quindi std::back_inserter è ciò di cui abbiamo bisogno:

std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
    std::back_inserter(s));

In alternativa, è possibile inizializzare una raccolta con dati di flusso, utilizzando un costruttore con argomenti dell'intervallo iteratore:

std::vector v(std::istreambuf_iterator<char>(ifs),
    std::istreambuf_iterator<char>());

Nota che questi esempi sono applicabili anche se ifs è aperto come file binario:

std::ifstream ifs("1.txt", std::ios::binary);

Copia di flussi

Un file può essere copiato su un altro file con stream e iteratori:

std::ofstream ofs("out.file");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
    std::ostream_iterator<char>(ofs));
ofs.close();

o reindirizzato a qualsiasi altro tipo di flusso con un'interfaccia compatibile. Ad esempio Stream di rete Boost.Asio:

boost::asio::ip::tcp::iostream stream;
stream.connect("example.com", "http");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
    std::ostream_iterator<char>(stream));
stream.close();

Array

Poiché gli iteratori possono essere pensati come una generalizzazione dei puntatori, i contenitori STL negli esempi sopra possono essere sostituiti con matrici native. Ecco come analizzare i numeri nell'array:

int arr[100];
std::copy(std::istream_iterator<char>(ifs), std::istream_iterator<char>(), arr);

Attenzione all'overflow del buffer, poiché gli array non possono essere ridimensionati al volo dopo che sono stati allocati. Ad esempio, se il codice sopra verrà alimentato con un file che contiene più di 100 numeri interi, tenterà di scrivere all'esterno della matrice e di eseguire un comportamento indefinito.

Stampa di collezioni con iostream

Stampa di base

std::ostream_iterator consente di stampare il contenuto di un container STL su qualsiasi flusso di output senza loop espliciti. Il secondo argomento del costruttore std::ostream_iterator imposta il delimitatore. Ad esempio, il seguente codice:

std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ! "));

stamperà

1 ! 2 ! 3 ! 4 !

Cast di tipo implicito

std::ostream_iterator consente di eseguire il cast del tipo di contenuto del contenitore implicitamente. Ad esempio, sintonizziamo std::cout per stampare valori a virgola mobile con 3 cifre dopo il punto decimale:

std::cout << std::setprecision(3);
std::fixed(std::cout);

e istanziare std::ostream_iterator con float , mentre i valori contenuti rimangono int :

std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<float>(std::cout, " ! "));

quindi il codice sopra produce

1.000 ! 2.000 ! 3.000 ! 4.000 !

nonostante std::vector detenga int s.

Generazione e trasformazione

std::generate funzioni std::generate , std::generate_n e std::transform forniscono uno strumento molto potente per la manipolazione dei dati al volo. Ad esempio, avendo un vettore:

std::vector<int> v = {1,2,3,4,8,16};

possiamo facilmente stampare il valore booleano dell'istruzione "x is even" per ogni elemento:

std::boolalpha(std::cout); // print booleans alphabetically
std::transform(v.begin(), v.end(), std::ostream_iterator<bool>(std::cout, " "),
[](int val) {
    return (val % 2) == 0;
});

o stampare l'elemento quadrato:

std::transform(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
    return val * val;
});

Stampa di numeri casuali delimitati da spazio:

const int N = 10;
std::generate_n(std::ostream_iterator<int>(std::cout, " "), N, std::rand);

Array

Come nella sezione sulla lettura dei file di testo, quasi tutte queste considerazioni possono essere applicate agli array nativi. Ad esempio, stampiamo i valori al quadrato da un array nativo:

int v[] = {1,2,3,4,8,16};
std::transform(v, std::end(v), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
    return val * val;
});

Analisi dei file

Analisi dei file nei contenitori STL

istream_iterator s sono molto utili per leggere sequenze di numeri o altri dati analizzabili in contenitori STL senza loop espliciti nel codice.

Utilizzando la dimensione del contenitore esplicita:

std::vector<int> v(100);
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
    v.begin());

o con l'inserimento di iteratore:

std::vector<int> v;
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
    std::back_inserter(v));

Si noti che i numeri nel file di input possono essere divisi per qualsiasi numero di caratteri di spazi vuoti e nuove righe.

Analisi di tabelle di testo eterogenee

Come istream::operator>> legge il testo fino ad un simbolo spazio bianco, può essere utilizzato in while condizioni per analizzare tabelle di dati complessi. Ad esempio, se abbiamo un file con due numeri reali seguiti da una stringa (senza spazi) su ogni riga:

1.12 3.14 foo
2.1 2.2 barr

può essere analizzato in questo modo:

std::string s;
double a, b;
while(ifs >> a >> b >> s) {
    std::cout << a << " " << b << " " << s << std::endl;
}

Trasformazione

Qualsiasi funzione di manipolazione degli intervalli può essere utilizzata con std::istream_iterator intervalli std::istream_iterator . Uno di questi è std::transform , che consente di elaborare i dati al volo. Ad esempio, leggiamo valori interi, moltiplicandoli per 3,14 e archiviamo il risultato in un contenitore a virgola mobile:

std::vector<double> v(100);
std::transform(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin(),
[](int val) {
    return val * 3.14;
});


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow