Zoeken…


Opmerkingen

Standaardconstructor van std::istream_iterator construeert een iterator die het einde van de stream vertegenwoordigt. Dus std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(), .... betekent kopiëren van de huidige positie in ifs tot het einde.

Stringstreams

std::ostringstream is een klasse waarvan de objecten eruit zien als een uitvoerstroom (dat wil zeggen, je kunt ze schrijven via operator<< ), maar de schrijfresultaten opslaan en ze in de vorm van een stroom aanbieden.

Overweeg de volgende korte code:

#include <sstream>
#include <string>                                                                                                                          

using namespace std;

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

De lijn

ostringstream ss;

maakt zo'n object. Dit object wordt eerst gemanipuleerd als een normale stream:

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

Hierna kan de resulterende stroom echter als volgt worden verkregen:

const string result = ss.str();

(het result de reeks is gelijk aan "the answer to everything is 42" ).

Dit is vooral handig als we een klasse hebben waarvoor streamserialisatie is gedefinieerd en waarvoor we een tekenreeksvorm willen. Stel bijvoorbeeld dat we wat klasse hebben

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

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

Om de stringvoorstelling van een foo object te krijgen,

foo f;

we zouden kunnen gebruiken

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

Het result bevat dan de stringvoorstelling van het foo object.

Een bestand lezen tot het einde

Een tekstbestand regel voor regel lezen

Een juiste manier om een tekstbestand regel voor regel tot het einde te lezen, is meestal niet duidelijk uit ifstream documentatie. Laten we eens kijken naar enkele veelgemaakte fouten die door beginnende C ++ -programmeurs zijn gemaakt en een goede manier om het bestand te lezen.

Lijnen zonder spaties

Laten we omwille van de eenvoud aannemen dat elke regel in het bestand geen witruimte-symbolen bevat.

ifstream heeft operator bool() , die true retourneert wanneer een stream geen fouten bevat en klaar is om te lezen. Bovendien ifstream::operator >> een verwijzing naar de stream zelf, zodat we EOF (en fouten) kunnen lezen en controleren op één regel met een zeer elegante syntaxis:

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

Lijnen met witruimtetekens

ifstream::operator >> leest de stream totdat er een spatie ontstaat, zodat de bovenstaande code de woorden van een regel op afzonderlijke regels afdrukt. Gebruik std::getline plaats van ifstream::operator >> om alles tot het einde van de regel te lezen. getline retourneert verwijzing naar de thread waarmee het werkte, dus dezelfde syntaxis is beschikbaar:

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

Het is duidelijk dat std::getline ook tot het einde moet worden gebruikt om een bestand met één regel te lezen.

Een bestand in één keer in een buffer lezen

Laten we tot slot het bestand van het begin tot het einde lezen zonder te stoppen bij een willekeurig teken, inclusief witruimten en nieuwe regels. Als we weten dat de exacte bestandsgrootte of de bovengrens van de lengte acceptabel is, kunnen we de tekenreeks verkleinen en vervolgens lezen:

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

Anders moeten we elk teken aan het einde van de tekenreeks invoegen, dus std::back_inserter is wat we nodig hebben:

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

Als alternatief is het mogelijk om een verzameling met stroomgegevens te initialiseren, met behulp van een constructor met iteratorbereikargumenten:

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

Merk op dat deze voorbeelden ook van toepassing zijn als ifs wordt geopend als binair bestand:

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

Streams kopiëren

Een bestand kan worden gekopieerd naar een ander bestand met streams en iterators:

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

of omgeleid naar een ander type stream met een compatibele interface. Bijvoorbeeld Boost.Asio-netwerkstream:

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();

arrays

Aangezien iterators kunnen worden beschouwd als een generalisatie van pointers, kunnen STL-containers in de bovenstaande voorbeelden worden vervangen door native arrays. Hier leest u hoe u getallen in een array kunt parseren:

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

Pas op voor buffer overflow, omdat arrays niet direct kunnen worden aangepast nadat ze zijn toegewezen. Als de bovenstaande code bijvoorbeeld wordt gevoed met een bestand dat meer dan 100 gehele getallen bevat, zal het proberen buiten de array te schrijven en ongedefinieerd gedrag vertonen.

Collecties afdrukken met iostream

Eenvoudig afdrukken

std::ostream_iterator maakt het mogelijk om de inhoud van een STL-container af te drukken naar elke uitvoer zonder expliciete lussen. Het tweede argument van std::ostream_iterator constructor stelt het scheidingsteken in. Bijvoorbeeld de volgende code:

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

zal afdrukken

1 ! 2 ! 3 ! 4 !

Impliciet type cast

std::ostream_iterator maakt het mogelijk het inhoudstype van de container impliciet te casten. Laten we bijvoorbeeld std::cout afstemmen om waarden met drijvende komma af te drukken met 3 cijfers achter de komma:

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

en std::ostream_iterator met float , terwijl de ingesloten waarden int blijven:

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

dus de bovenstaande code levert op

1.000 ! 2.000 ! 3.000 ! 4.000 !

ondanks std::vector bevat int s.

Generatie en transformatie

std::generate , std::generate_n en std::transform bieden een zeer krachtig hulpmiddel voor on-the-fly gegevensmanipulatie. Bijvoorbeeld, met een vector:

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

we kunnen eenvoudig de booleaanse waarde van de "x is even" -instructie voor elk element afdrukken:

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;
});

of druk het vierkante element af:

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

N-door spaties gescheiden willekeurige getallen afdrukken:

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

arrays

Net als in het gedeelte over het lezen van tekstbestanden, kunnen bijna al deze overwegingen worden toegepast op native arrays. Laten we bijvoorbeeld vierkante waarden afdrukken vanuit een native array:

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;
});

Bestanden parseren

Bestanden in STL-containers parseren

istream_iterator zijn erg handig voor het lezen van reeksen getallen of andere ontleedbare gegevens in STL-containers zonder expliciete lussen in de code.

Gebruik expliciete containergrootte:

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

of met het invoegen van iterator:

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

Merk op dat de getallen in het invoerbestand kunnen worden gedeeld door een willekeurig aantal witruimte-tekens en nieuwe regels.

Heterogene teksttabellen parseren

Zoals istream::operator>> leest de tekst tot een witruimte symbool, kan het worden gebruikt in while conditie te ontleden complexe gegevens tabellen. Als we bijvoorbeeld een bestand hebben met twee reële getallen gevolgd door een string (zonder spaties) op elke regel:

1.12 3.14 foo
2.1 2.2 barr

het kan als volgt worden ontleed:

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

transformatie

Elke bereikmanipulatiefunctie kan worden gebruikt met std::istream_iterator bereiken. Een daarvan is std::transform , waarmee gegevens direct kunnen worden std::transform . Laten we bijvoorbeeld gehele waarden lezen, ze vermenigvuldigen met 3.14 en het resultaat opslaan in een container met drijvende komma:

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow