C++
C ++ strömmar
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;
});