Szukaj…


Uwagi

Domyślny konstruktor std::istream_iterator konstruuje iterator, który reprezentuje koniec strumienia. Zatem std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(), .... oznacza kopiowanie z bieżącej pozycji w ifs do końca.

Strumienie ciągów

std::ostringstream to klasa, której obiekty wyglądają jak strumień wyjściowy (tzn. można do nich pisać za pomocą operator<< ), ale w rzeczywistości przechowują wyniki zapisu i udostępniają je w postaci strumienia.

Rozważ następujący krótki kod:

#include <sstream>
#include <string>                                                                                                                          

using namespace std;

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

Linia

ostringstream ss;

tworzy taki obiekt. Ten obiekt jest najpierw manipulowany jak zwykły strumień:

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

Jednak po tym wynikowy strumień można uzyskać w następujący sposób:

const string result = ss.str();

( result ciągu będzie równy "the answer to everything is 42" ).

Jest to szczególnie przydatne, gdy mamy klasę, dla której zdefiniowano serializację strumienia i dla której potrzebujemy postaci ciągu. Załóżmy na przykład, że mamy klasę

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

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

Aby uzyskać ciąg znaków reprezentujący obiekt foo ,

foo f;

moglibyśmy użyć

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

Następnie result zawiera ciąg znaków reprezentujący obiekt foo .

Czytanie pliku do końca

Czytanie pliku tekstowego wiersz po wierszu

Właściwy sposób czytania pliku tekstowego linia po linii do końca zwykle nie wynika z dokumentacji ifstream . Rozważmy kilka typowych błędów popełnianych przez początkujących programistów C ++ i właściwy sposób odczytu pliku.

Linie bez znaków białych znaków

Dla uproszczenia załóżmy, że każda linia w pliku nie zawiera symboli białych znaków.

ifstream ma operator bool() , który zwraca true, gdy strumień nie zawiera błędów i jest gotowy do odczytu. Ponadto ifstream::operator >> zwraca odwołanie do samego strumienia, dzięki czemu możemy odczytać i sprawdzić EOF (jak również błędy) w jednym wierszu z bardzo elegancką składnią:

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

Linie ze znakami spacji

ifstream::operator >> czyta strumień, aż pojawi się jakikolwiek biały znak, więc powyższy kod wydrukuje słowa z linii w osobnych wierszach. Aby przeczytać wszystko do końca linii, użyj std::getline zamiast ifstream::operator >> . getline zwraca odwołanie do wątku, z którym pracował, więc dostępna jest ta sama składnia:

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

Oczywiście std::getline powinien być również używany do odczytu pliku jednowierszowego do końca.

Odczytywanie pliku do bufora jednocześnie

Na koniec przeczytajmy plik od początku do końca, nie zatrzymując się na żadnym znaku, w tym białych znakach i znakach nowej linii. Jeśli wiemy, że dokładny rozmiar pliku lub górna granica długości jest dopuszczalna, możemy zmienić rozmiar łańcucha, a następnie przeczytać:

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

W przeciwnym razie musimy wstawić każdy znak na końcu łańcucha, więc potrzebujemy std::back_inserter :

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

Alternatywnie można zainicjować kolekcję danymi strumienia, używając konstruktora z argumentami zakresu iteratora:

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

Pamiętaj, że te przykłady mają również zastosowanie, jeśli ifs jest otwarty jako plik binarny:

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

Kopiowanie strumieni

Plik można skopiować do innego pliku za pomocą strumieni i iteratorów:

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

lub przekierowane do dowolnego innego rodzaju strumienia z kompatybilnym interfejsem. Na przykład strumień sieciowy 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();

Tablice

Ponieważ iteratory można uznać za uogólnienie wskaźników, kontenery STL w powyższych przykładach można zastąpić rodzimymi tablicami. Oto jak parsować liczby w tablicy:

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

Uważaj na przepełnienie bufora, ponieważ nie można zmienić rozmiaru tablic w locie po ich przydzieleniu. Na przykład, jeśli powyższy kod zostanie zasilony plikiem zawierającym więcej niż 100 liczb całkowitych, spróbuje zapisać poza tablicą i zachowa się w nieokreślony sposób.

Drukowanie kolekcji za pomocą iostream

Podstawowe drukowanie

std::ostream_iterator pozwala wydrukować zawartość kontenera STL do dowolnego strumienia wyjściowego bez wyraźnych pętli. Drugi argument konstruktora std::ostream_iterator ustawia ogranicznik. Na przykład następujący kod:

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

wydrukuje

1 ! 2 ! 3 ! 4 !

Rzutowany typ niejawny

std::ostream_iterator pozwala std::ostream_iterator rzutować typ zawartości kontenera. Na przykład, dostrójmy std::cout aby drukować wartości zmiennoprzecinkowe z 3 cyframi po przecinku:

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

i std::ostream_iterator instancję std::ostream_iterator z float , podczas gdy zawarte wartości pozostają int :

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

więc powyższy kod daje wynik

1.000 ! 2.000 ! 3.000 ! 4.000 !

pomimo std::vector posiada int s.

Generacja i transformacja

Funkcje std::generate , std::generate_n i std::transform stanowią bardzo potężne narzędzie do manipulacji danymi w locie. Na przykład mając wektor:

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

możemy łatwo wydrukować wartość logiczną instrukcji „x jest parzyste” dla każdego elementu:

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

lub wydrukuj kwadratowy element:

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

Drukowanie N liczb losowych rozdzielanych spacjami:

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

Tablice

Podobnie jak w części dotyczącej czytania plików tekstowych, prawie wszystkie te kwestie można zastosować do macierzy natywnych. Na przykład wypiszmy kwadratowe wartości z natywnej tablicy:

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

Parsowanie plików

Parsowanie plików do kontenerów STL

istream_iterator s są bardzo przydatne do odczytywania sekwencji liczb lub innych analizowalnych danych do kontenerów STL bez wyraźnych pętli w kodzie.

Używając jawnego rozmiaru kontenera:

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

lub z wstawieniem iteratora:

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

Zauważ, że liczby w pliku wejściowym mogą być podzielone przez dowolną liczbę dowolnych białych znaków i znaków nowej linii.

Analizowanie heterogenicznych tabel tekstowych

Ponieważ istream::operator>> odczytuje tekst aż do spacji, można go używać w warunku while do analizowania złożonych tabel danych. Na przykład, jeśli mamy plik z dwiema liczbami rzeczywistymi, po których następuje ciąg (bez spacji) w każdym wierszu:

1.12 3.14 foo
2.1 2.2 barr

można go sparsować w następujący sposób:

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

Transformacja

std::istream_iterator zakresami std::istream_iterator można używać dowolnej funkcji manipulowania zakresem. Jednym z nich jest std::transform , który pozwala przetwarzać dane w locie. Na przykład, przeczytajmy wartości całkowite, pomnóż je przez 3,14 i zapisz wynik w pojemniku zmiennoprzecinkowym:

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow