C++
C ++ Streams
Suche…
Bemerkungen
Der Standardkonstruktor von std::istream_iterator
einen Iterator, der das Ende des Streams darstellt. Daher bedeutet std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(), ....
das Kopieren von der aktuellen Position in ifs
zum Ende.
String-Streams
std::ostringstream
ist eine Klasse, deren Objekte wie ein Ausgabestrom aussehen ( std::ostringstream
Sie können sie mit dem operator<<
schreiben), sie speichern jedoch die Schreibergebnisse und stellen sie in Form eines Streams bereit.
Betrachten Sie den folgenden Kurzcode:
#include <sstream>
#include <string>
using namespace std;
int main()
{
ostringstream ss;
ss << "the answer to everything is " << 42;
const string result = ss.str();
}
Die Linie
ostringstream ss;
erstellt ein solches Objekt. Dieses Objekt wird zuerst wie ein regulärer Stream bearbeitet:
ss << "the answer to everything is " << 42;
Im Anschluss daran kann der resultierende Stream jedoch folgendermaßen erhalten werden:
const string result = ss.str();
(Das String- result
ist gleich "the answer to everything is 42"
).
Dies ist vor allem dann nützlich, wenn wir eine Klasse haben, für die Stream-Serialisierung definiert wurde und für die wir ein String-Formular haben möchten. Angenommen, wir haben eine Klasse
class foo
{
// All sort of stuff here.
};
ostream &operator<<(ostream &os, const foo &f);
Um die Zeichenfolgendarstellung eines foo
Objekts zu erhalten,
foo f;
wir könnten gebrauchen
ostringstream ss;
ss << f;
const string result = ss.str();
Das result
enthält dann die Zeichenfolgendarstellung des foo
Objekts.
Lesen einer Datei bis zum Ende
Eine Textdatei zeilenweise lesen
Eine geeignete Methode, eine Textdatei Zeile für Zeile bis zum Ende zu lesen, ist in der Regel nicht aus der Dokumentation von ifstream
. Betrachten wir einige häufige Fehler, die von Anfängern von C ++ - Programmierern begangen wurden, und eine geeignete Methode zum Lesen der Datei.
Zeilen ohne Whitespace-Zeichen
Der Einfachheit halber nehmen wir an, dass jede Zeile in der Datei keine Leerzeichen enthält.
ifstream
hat den operator bool()
, der true zurückgibt, wenn ein Stream fehlerfrei ist und zum Lesen bereit ist. Darüber hinaus gibt ifstream::operator >>
einen Verweis auf den Stream selbst zurück, sodass wir EOF (sowie Fehler) in einer Zeile mit sehr eleganter Syntax lesen und überprüfen können:
std::ifstream ifs("1.txt");
std::string s;
while(ifs >> s) {
std::cout << s << std::endl;
}
Zeilen mit Whitespace-Zeichen
ifstream::operator >>
liest den Stream, bis ein Leerzeichen auftritt, sodass der obige Code die Wörter aus einer Zeile in separaten Zeilen druckt. Um alles bis zum Zeilenende zu lesen, verwenden Sie std::getline
anstelle von ifstream::operator >>
. getline
gibt den Verweis auf den Thread zurück, mit dem er gearbeitet hat, daher ist dieselbe Syntax verfügbar:
while(std::getline(ifs, s)) {
std::cout << s << std::endl;
}
std::getline
sollte std::getline
auch bis zum Ende zum Lesen einer einzeiligen Datei verwendet werden.
Eine Datei sofort in einen Puffer lesen
Schließlich lesen wir die Datei vom Anfang bis zum Ende, ohne bei einem beliebigen Zeichen, einschließlich Leerzeichen und Zeilenumbrüchen, anzuhalten. Wenn wir wissen, dass die genaue Dateigröße oder Obergrenze der Länge akzeptabel ist, können wir die Größe der Zeichenfolge ändern und dann lesen:
s.resize(100);
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
s.begin());
Andernfalls müssen wir jedes Zeichen an das Ende der Zeichenfolge einfügen. std::back_inserter
benötigen wir std::back_inserter
:
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::back_inserter(s));
Alternativ ist es möglich, eine Sammlung mit Streamdaten zu initialisieren, indem ein Konstruktor mit Iteratorbereichsargumenten verwendet wird:
std::vector v(std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>());
Beachten Sie, dass diese Beispiele auch anwendbar sind, wenn ifs
als Binärdatei geöffnet ist:
std::ifstream ifs("1.txt", std::ios::binary);
Streams kopieren
Eine Datei kann in eine andere Datei mit Streams und Iteratoren kopiert werden:
std::ofstream ofs("out.file");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(ofs));
ofs.close();
oder mit einer kompatiblen Schnittstelle zu einem anderen Stream-Typ umgeleitet. Beispiel: Boost.Asio-Netzwerkstream:
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
Da Iteratoren als Verallgemeinerung von Zeigern betrachtet werden können, können STL-Container in den obigen Beispielen durch native Arrays ersetzt werden. So analysieren Sie Zahlen in ein Array:
int arr[100];
std::copy(std::istream_iterator<char>(ifs), std::istream_iterator<char>(), arr);
Beachten Sie den Pufferüberlauf, da die Größe der Arrays nach der Zuweisung nicht sofort geändert werden kann. Wenn der obige Code beispielsweise mit einer Datei gespeist wird, die mehr als 100 Integer-Zahlen enthält, wird versucht, außerhalb des Arrays zu schreiben und in undefiniertes Verhalten zu geraten.
Kollektionen mit Iostream drucken
Grundlegendes Drucken
std::ostream_iterator
können Sie den Inhalt eines STL-Containers ohne explizite Schleifen in einen beliebigen Ausgabestrom drucken. Das zweite Argument des std::ostream_iterator
Konstruktors legt das Trennzeichen fest. Zum Beispiel den folgenden Code:
std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ! "));
wird drucken
1 ! 2 ! 3 ! 4 !
Implizite Typumwandlung
std::ostream_iterator
kann der Inhaltstyp des Containers implizit umgewandelt werden. std::cout
wir zum Beispiel std::cout
, um Fließkommazahlen mit 3 Stellen nach dem Dezimalpunkt zu drucken:
std::cout << std::setprecision(3);
std::fixed(std::cout);
und instanziieren std::ostream_iterator
mit float
, während die enthaltenen Werte int
bleiben:
std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<float>(std::cout, " ! "));
der obige Code ergibt also
1.000 ! 2.000 ! 3.000 ! 4.000 !
trotz std::vector
gilt int
s.
Erzeugung und Transformation
std::generate
, std::generate_n
und std::transform
bieten ein sehr leistungsfähiges Werkzeug für die schnelle Datenmanipulation. Zum Beispiel mit einem Vektor:
std::vector<int> v = {1,2,3,4,8,16};
Wir können den booleschen Wert der Anweisung "x is even" für jedes Element einfach drucken:
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;
});
oder drucken Sie das quadratische Element:
std::transform(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
return val * val;
});
N durch Leerzeichen getrennte Zufallszahlen drucken:
const int N = 10;
std::generate_n(std::ostream_iterator<int>(std::cout, " "), N, std::rand);
Arrays
Wie im Abschnitt zum Lesen von Textdateien können fast alle diese Überlegungen auf native Arrays angewendet werden. Lassen Sie uns zum Beispiel quadratische Werte aus einem nativen Array drucken:
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;
});
Dateien analysieren
Dateien in STL-Container analysieren
istream_iterator
s ist sehr nützlich, um Zahlenfolgen oder andere analysierbare Daten in STL-Container ohne explizite Schleifen im Code zu lesen.
Explizite Containergröße verwenden:
std::vector<int> v(100);
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin());
oder beim Einfügen des Iterators:
std::vector<int> v;
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
std::back_inserter(v));
Beachten Sie, dass die Zahlen in der Eingabedatei durch eine beliebige Anzahl von Leerzeichen und Zeilenumbrüchen geteilt werden können.
Analyse heterogener Texttabellen
Da istream::operator>>
Text bis zu einem Leerzeichen liest, kann er in while
werden, um komplexe Datentabellen zu parsen. Wenn wir zum Beispiel eine Datei mit zwei reellen Zahlen haben, gefolgt von einer Zeichenfolge (ohne Leerzeichen) in jeder Zeile:
1.12 3.14 foo
2.1 2.2 barr
Es kann wie folgt analysiert werden:
std::string s;
double a, b;
while(ifs >> a >> b >> s) {
std::cout << a << " " << b << " " << s << std::endl;
}
Transformation
std::istream_iterator
Bereichen std::istream_iterator
kann jede Bereichsmanipulationsfunktion verwendet werden. Eines davon ist std::transform
, mit dem Daten im laufenden Betrieb verarbeitet werden können. Lassen Sie uns beispielsweise Ganzzahlwerte lesen, mit 3,14 multiplizieren und das Ergebnis in einem Gleitkommabehälter speichern:
std::vector<double> v(100);
std::transform(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin(),
[](int val) {
return val * 3.14;
});