Rolę plików mogą też pełnić napisy, czyli tablice znaków w stylu C i napisy w stylu C++, a więc obiekty klasy string.
Napis w stylu C jest ciągłym obszarem pamięci, do którego odnosimy się jak do tablicy znaków. Obszar ten może być traktowany jako plik, do którego dane mogą być zapisywane lub z którego mogą być odczytywane.
Aby korzystać z tego rodzaju „plików”, należy dołączyć plik nagłówkowy strstream, który daje nam dostęp do klasy strumieniowej wejściowej istrstream i wyjściowej ostrstream.
Obiekt strumieniowy klasy wyjściowej ostrstream tworzymy na dwa sposoby:
1. #include <iostream> 2. #include <strstream> 3. #include <cstdlib> // free 4. using namespace std; 5. 6. int main() { 7. // wersja "gumowa" 8. ostrstream napis1; 9. napis1 << "Poczatek, " << "dalszy ciag, " 10. << "koniec." << ends; 11. char* n = napis1.str(); 12. cout << "Napis jest: " << n << endl; 13. free(n); 14. 15. // wersja tablicowa 16. char tab[30]; 17. ostrstream napis2(tab,sizeof(tab)); 18. napis2 << "Magda " << "Kasia " << "Marta" << ends; 19. cout << tab << endl; 20. }
Napis jest: Poczatek, dalszy ciag, koniec. Magda Kasia MartaZauważmy, że pamięci na napis n nie alokujemy. Jest ona alokowana automatycznie i jej adres jest zwracany przez metodę str (linia 11).
Podobnie można z tablicy znaków czytać, tworząc obiekt klasy istrstream.
Pliki wewnętrzne oparte na C-napisach są czasem używane do reprezentowania pliku dyskowego, szczególnie jeśli zachodzi potrzeba częstego „skakania” do różnych części pliku, co może być drogie, gdyż wymaga wielu operacji dostępu do pliku. Może wtedy być bardziej opłacalne wczytanie całego pliku do tablicy znaków i dalej używanie jej jak pliku wewnętrznego.
Podobnie jak tablice znaków, do reprezentowania plików wewnętrznych mogą być używane obiekty klasy string. Jest to łatwiejsze i bezpieczniejsze niż używanie tablic znaków.
Aby korzystać z tego udogodnienia, należy dołączyć, prócz pliku string, plik nagłówkowy sstream. Zdefiniowane tam są klasy strumieniowe wejściowa istringstream i wyjściowa ostringstream korzystające z napisów klasy string.
Strumień wyjściowy można utworzyć za pomocą konstruktora domyślnego:
ostringstream strm;i pisać do niego jak do normalnego strumienia
strm << "To be" << " or not to be" << ", etc.";Nie trzeba martwić się o przepełnienie, bo pamięć w miarę potrzeby jest alokowana automatycznie. Po zakończeniu pisania do strumienia, wywołujemy na jego rzecz metodę str, która zwraca napis (obiekt klasy string) zawierający rezultat, na przykład:
string s = strm.str(); cout << s << endl;Można też skonstruować strumień z wpisanym już fragmentem w trybie dopisywania na końcu
ostringstream ostr("Jakis napis", ios::ate); ostr << "Dalsza czesc";a następnie dopisywać dalsze fragmenty, jak w przykładzie powyżej.
Istniejący obiekt klasy
string
też może być otwarty
jako wejściowy plik wewnętrzny, jak w przykładzie poniżej:
1. #include <iostream> 2. #include <sstream> 3. using namespace std; 4. 5. void words(const string& s) { 6. istringstream istr(s); 7. 8. string word; 9. while ( istr >> word ) 10. cout << word << endl; 11. } 12. 13. int main() { 14. string s = "Bach Haydn"; 15. ostringstream ostr(s, ios::ate); 16. ostr << " Chopin"; 17. string s1 = ostr.str(); 18. words(s1); 19. }
Bach Haydn Chopin
Pokażemy jeszcze, jak czytać z i zapisywać do plików tekstowych.
Spójrzmy na przykład
1. #include <iostream> 2. #include <fstream> // ifstream, ofstream 3. #include <string> 4. #include <sstream> // stringstream 5. using namespace std; 6. 7. int main() { 8. string line{}; 9. 10. ofstream outf{"RWfile.out"}; ➊ 11. for (ifstream in{"RWfile.dat"}; getline(in, line);) { ➋ 12. cout << line << "\n"; 13. string name; 14. int height; 15. double weight; 16. istringstream str{line}; ➌ 17. str >> name >> height >> weight; 18. cout << name << ": height=" << height 19. << ", weight=" << weight << '\n'; 20. outf << name << ": height=" << height ➍ 21. << ", weight=" << weight << '\n'; 22. } 23. outf.close(); 24. 25. // again but in a while loop 26. ifstream in{"RWfile.dat"}; 27. while (getline(in, line)) { 28. cout << line << "\n"; 29. } 30. }
W linii ➊ tworzymy obiekt
outf
typu
ofstream
('of' od output stream).
Obiekt ten reprezentuje strumień wyjściowy, jak
cout
ale związany z plikiem
RWfile.out
(który zostanie
utworzony, jeśli nie istnieje). Jak widzimy w linii ➍,
używamy
outf
dokładnie jak
cout, ale
tekst jest zapisywany w pliku, a nie na ekranie.
W linii ➋ tworzymy obiekt
in
typu
ifstream
— to jest strumień wejściowy, jak
cin
ale
dla którego dane są wczytywane z pliku a nie z klawiatury.
Funkcja
getline
(z nagłówka
string
header)
pobiera strumień wejściowy i obiekt typu
string
(w naszym przypadku
line), czyta jeden wiersz
do
line. Zwraca otrzymany strumień, który jest
konwertowalny do wartości logicznej, która będzie
false
gdy napotkany zostanie koniec pliku.
Object
str
(➌) jest typu
istringstream
(z nagłówka
sstream) i reprezentuje strumień wejściowy,
dla którego źródłem jest przekazany do konstruktora napis
(w naszym przypadku
line). Jak widzimy możemy
używać tego obiektu jak
cin
aby wczytać dane
z
line.
Z następującymi danymi w pliku
RWfile.dat
Mary 167 56.5 Jane 162 55.7 Kate 170 59.1program wypisuje na ekran i do pliku RWfile.out
Mary 167 56.5 Mary: height=167, weight=56.5 Jane 162 55.7 Jane: height=162, weight=55.7 Kate 170 59.1 Kate: height=170, weight=59.1 Mary 167 56.5 Jane 162 55.7 Kate 170 59.1
T.R. Werner, 23 lutego 2022; 19:40