Sök…


Anmärkningar

Standardkonstruktör för std::istream_iterator konstruerar en iterator som representerar strömmens slut. Således std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(), .... att kopiera från den aktuella positionen i ifs till slutet.

Strängströmmar

std::ostringstream är en klass vars objekt ser ut som en utgångsström (det vill säga du kan skriva till dem via operator<< ), men faktiskt lagra skrivresultaten och ge dem i form av en ström.

Tänk på följande kortkod:

#include <sstream>
#include <string>                                                                                                                          

using namespace std;

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

Linjen

ostringstream ss;

skapar ett sådant objekt. Detta objekt manipuleras först som en vanlig ström:

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

Efter detta kan emellertid den resulterande strömmen erhållas på följande sätt:

const string result = ss.str();

(strängen result kommer att vara lika med "the answer to everything is 42" ).

Detta är främst användbart när vi har en klass för vilken strömserialisering har definierats och som vi vill ha en strängform. Antag till exempel att vi har lite klass

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

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

För att få strängrepresentationen av ett foo objekt,

foo f;

vi kunde använda

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

Därefter innehåller result strängrepresentation av foo objektet.

Läser en fil till slutet

Läsa en textfil rad för rad

Ett ordentligt sätt att läsa en textfil rad för rad fram till slutet är vanligtvis inte tydligt från ifstream dokumentationen. Låt oss överväga några vanliga misstag som gjorts av nybörjare C ++ -programmerare och ett korrekt sätt att läsa filen.

Linjer utan blankstegstecken

För enkelhets skull, låt oss anta att varje rad i filen inte innehåller några blankstegssymboler.

ifstream har operator bool() , vilket returnerar sant när en ström inte har några fel och är redo att läsas. Dessutom ifstream::operator >> en referens till själva strömmen, så att vi kan läsa och kontrollera om EOF (såväl som för fel) i en rad med mycket elegant syntax:

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

Linjer med blankstegstecken

ifstream::operator >> läser strömmen tills något whitespace-tecken inträffar, så ovanstående kod kommer att skriva ut orden från en rad på separata rader. För att läsa allt till slutet av raden använder du std::getline istället för ifstream::operator >> . getline returnerar referens till tråden den fungerade med, så samma syntax är tillgänglig:

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

Självklart bör std::getline också användas för att läsa en fil med en rad till slutet.

Läser en fil i en buffert på en gång

Slutligen, låt oss läsa filen från början till slutet utan att stoppa på någon karaktär, inklusive blanksteg och nylinjer. Om vi vet att den exakta filstorleken eller den övre gränsen för längden är acceptabel kan vi ändra storlek på strängen och sedan läsa:

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

Annars måste vi infoga varje tecken i slutet av strängen, så std::back_inserter är vad vi behöver:

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

Alternativt är det möjligt att initiera en samling med strömdata med hjälp av en konstruktör med iteratorintervallargument:

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

Observera att dessa exempel också är tillämpliga om ifs öppnas som binär fil:

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

Kopierar strömmar

En fil kan kopieras till en annan fil med strömmar och iteratorer:

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

eller omdirigeras till någon annan typ av ström med ett kompatibelt gränssnitt. Till exempel Boost.Asio nätverksström:

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

arrayer

Eftersom iteratorer kan betraktas som en generalisering av pekare, kan STL-behållare i exemplen ovan ersättas med infödda matriser. Så här analyserar du siffror i array:

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

Se upp för buffertöverskridning, eftersom matriser inte kan ändras i farten efter att de tilldelats. Om till exempel koden ovan matas med en fil som innehåller mer än 100 heltal, kommer den att försöka skriva utanför matrisen och stöta på odefinierat beteende.

Skriva ut samlingar med iostream

Grundläggande utskrift

std::ostream_iterator gör det möjligt att skriva ut innehållet i en STL-behållare till vilken utgångsström som helst utan uttryckliga slingor. Det andra argumentet för std::ostream_iterator konstruktör ställer in avgränsaren. Till exempel följande kod:

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

kommer att skriva ut

1 ! 2 ! 3 ! 4 !

Implicit typspelning

std::ostream_iterator gör det möjligt att casta innehållets innehåll implicit. Låt oss till exempel ställa in std::cout att skriva ut flytande punktvärden med 3 siffror efter decimalpunkt:

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

och omedelbart std::ostream_iterator med float , medan de innehållande värdena förblir int :

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

så koden ovan ger

1.000 ! 2.000 ! 3.000 ! 4.000 !

trots std::vector håller int .

Generation och transformation

std::generate , std::generate_n och std::transform ger ett mycket kraftfullt verktyg för on-the-fly data manipulation. Att till exempel ha en vektor:

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

vi kan enkelt skriva ut booleskt värde för "x är jämnt" -sats för varje element:

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

eller skriv ut det kvadratiska elementet:

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

Skriva ut N-avgränsade slumpmässiga nummer:

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

arrayer

Liksom i avsnittet om läsning av textfiler kan nästan alla dessa överväganden tillämpas på inbyggda matriser. Låt oss till exempel skriva ut kvadratvärden från en inbyggd matris:

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

Analysera filer

Parsar filer i STL-containrar

istream_iterator s är mycket användbara för att läsa sekvenser av siffror eller annan parsbar data i STL-behållare utan uttryckliga slingor i koden.

Använda tydlig behållarstorlek:

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

eller med insättning av iterator:

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

Observera att siffrorna i inmatningsfilen kan delas med valfritt antal vitrumstecken och nyrader.

Analysera heterogena texttabeller

Som istream::operator>> läser text tills en blank symbol, kan det användas i while tillstånd för att tolka komplexa datatabeller. Om vi till exempel har en fil med två riktiga nummer följt av en sträng (utan mellanslag) på varje rad:

1.12 3.14 foo
2.1 2.2 barr

det kan analyseras så här:

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

Omvandling

Varje intervallmanipulerande funktion kan användas med std::istream_iterator intervall. En av dem är std::transform , som gör det möjligt att bearbeta data on-the-fly. Låt oss till exempel läsa heltalvärden, multiplicera dem med 3.14 och lagra resultatet i flytande punktbehållare:

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow