W tym w tej sekcji przyjrzymy się różnym aspektom klasy wyjściowej iostream (ostream).
Operator wstawiania
Operator wstawiania (<<) służy do umieszczania informacji w strumieniu wyjściowym. C++ ma predefiniowane operacje wstawiania dla wszystkich wbudowanych typów danych i już widziałeś, jak przeciążyć operator wstawiania dla własnych klas.
W lekcji na temat strumieni zobaczyłeś, że zarówno istream, jak i ostream wywodzą się z klasy zwanej ios. Jednym z zadań systemu ios (oraz ios_base) jest kontrolowanie opcji formatowania danych wyjściowych.
Formatowanie
Istnieją dwa sposoby zmiany opcji formatowania: flagi i manipulatory. Można myśleć o flagach jako zmiennych logicznych, które można włączać i wyłączać. Manipulatory to obiekty umieszczone w strumieniu, które wpływają na sposób wprowadzania i wyprowadzania danych.
Aby włączyć flagę, użyj funkcję setf() z odpowiednią flagą jako parametrem. Na przykład domyślnie C++ nie drukuje znaku + przed liczbami dodatnimi. Jednakże używając flagi std::ios::showpos, możemy zmienić to zachowanie:
std::cout.setf(std::ios::showpos); // turn on the std::ios::showpos flag
std::cout << 27 << '\n';To daje następujący efekt wyjście:
+27
Możliwe jest włączenie wielu flag ios jednocześnie za pomocą operatora Bitwise OR (|):
std::cout.setf(std::ios::showpos | std::ios::uppercase); // turn on the std::ios::showpos and std::ios::uppercase flag
std::cout << 1234567.89f << '\n';To daje:
+1.23457E+06
Aby wyłączyć flagę, użyj unsetf() funkcji:
std::cout.setf(std::ios::showpos); // turn on the std::ios::showpos flag
std::cout << 27 << '\n';
std::cout.unsetf(std::ios::showpos); // turn off the std::ios::showpos flag
std::cout << 28 << '\n';To daje następujący efekt wyjście:
+27 28
Z użyciem setf() wiąże się jeszcze jedna trudność, o której należy wspomnieć. Wiele flag należy do grup zwanych grupami formatowymi. Grupa formatów to grupa flag, które realizują podobne (czasami wykluczające się) opcje formatowania. Na przykład grupa formatów o nazwie „pole bazowe” zawiera flagi „oct”, „dec” i „hex”, które kontrolują podstawę wartości całkowitych. Domyślnie ustawiona jest flaga „dec”. W rezultacie, jeśli to zrobimy:
std::cout.setf(std::ios::hex); // try to turn on hex output
std::cout << 27 << '\n';Otrzymamy następujący wynik:
27
To nie zadziałało! Powodem jest to, że setf() włącza tylko flagi — nie jest wystarczająco inteligentna, aby wyłączać wzajemnie wykluczające się flagi. W rezultacie, kiedy włączyliśmy std::hex, std::ios::dec był nadal włączony, a std::ios::dec najwyraźniej ma pierwszeństwo. Istnieją dwa sposoby obejścia tego problemu.
Po pierwsze możemy wyłączyć std::ios::dec, tak aby ustawione było tylko std::hex:
std::cout.unsetf(std::ios::dec); // turn off decimal output
std::cout.setf(std::ios::hex); // turn on hexadecimal output
std::cout << 27 << '\n';Teraz otrzymamy wynik zgodnie z oczekiwaniami:
1b
Drugi sposób polega na użyciu innej formy setf(), która przyjmuje dwa parametry: pierwszy parametr to flaga do ustawienia, a drugi to grupa formatowania, do której należy. Podczas korzystania z tej formy setf() wszystkie flagi należące do grupy są wyłączane, a włączana jest tylko flaga przekazana. Na przykład:
// Turn on std::ios::hex as the only std::ios::basefield flag
std::cout.setf(std::ios::hex, std::ios::basefield);
std::cout << 27 << '\n';To również daje oczekiwany wynik:
1b
Używanie setf() i unsetf() jest zwykle niewygodne, więc C++ zapewnia drugi sposób zmiany opcji formatowania: manipulatory. Zaletą manipulatorów jest to, że są wystarczająco inteligentne, aby włączać i wyłączać odpowiednie flagi. Oto przykład wykorzystania niektórych manipulatorów do zmiany bazy:
std::cout << std::hex << 27 << '\n'; // print 27 in hex
std::cout << 28 << '\n'; // we're still in hex
std::cout << std::dec << 29 << '\n'; // back to decimalTen program generuje wynik:
1b 1c 29
Generalnie użycie manipulatorów jest znacznie łatwiejsze niż ustawianie i rozbrajanie flag. Wiele opcji jest dostępnych zarówno za pośrednictwem flag, jak i manipulatorów (takich jak zmiana bazy), jednak inne opcje są dostępne tylko za pośrednictwem flag lub manipulatorów, dlatego ważne jest, aby wiedzieć, jak używać obu.
Przydatne formatery
Oto lista niektórych z bardziej przydatnych flag, manipulatorów i funkcji składowych. Flagi znajdują się w klasie std::ios, manipulatory w przestrzeni nazw std, a funkcje składowe w klasie std::ostream.
| Grupa | Flag | Znaczenie |
|---|---|---|
| std::ios::boolalpha | Jeśli ustawione, wartości logiczne wyświetlają wartość „prawda” lub „fałsz”. Jeśli nie ustawiono, wartości logiczne wypisują 0 lub 1 |
| Manipulator | Znaczenie |
|---|---|
| std::boolalpha | Booleany wypisują „prawda” lub „fałsz” |
| std::noboolalpha | Boolean wypisują 0 lub 1 (domyślnie) |
Przykład:
std::cout << true << ' ' << false << '\n';
std::cout.setf(std::ios::boolalpha);
std::cout << true << ' ' << false << '\n';
std::cout << std::noboolalpha << true << ' ' << false << '\n';
std::cout << std::boolalpha << true << ' ' << false << '\n';Wynik:
1 0 true false 1 0 true false
| Grupa | Flag | Znaczenie |
|---|---|---|
| std::ios::showpos | Jeśli ustawione, liczby dodatnie poprzedź znakiem + |
| Manipulator | Znaczenie |
|---|---|
| std::showpos | Poprzedza liczby dodatnie + |
| std::noshowpos | Nie poprzedza liczb dodatnich + |
Przykład:
std::cout << 5 << '\n';
std::cout.setf(std::ios::showpos);
std::cout << 5 << '\n';
std::cout << std::noshowpos << 5 << '\n';
std::cout << std::showpos << 5 << '\n';Wynik:
5 +5 5 +5
| Grupa | Flag | Znaczenie |
|---|---|---|
| std::ios::uppercase | Jeśli ustawione, używa wielkich liter litery |
| Manipulator | Znaczenie |
|---|---|
| std::uppercase | Używa wielkich liter |
| std::nouppercase | Używa małych liter litery |
Przykład:
std::cout << 12345678.9 << '\n';
std::cout.setf(std::ios::uppercase);
std::cout << 12345678.9 << '\n';
std::cout << std::nouppercase << 12345678.9 << '\n';
std::cout << std::uppercase << 12345678.9 << '\n';Wynik:
1.23457e+007 1.23457E+007 1.23457e+007 1.23457E+007
| Grupa | Flag | Znaczenie |
|---|---|---|
| std::ios::basefield | std::ios::dec | Drukuje wartości w formacie dziesiętnym (domyślnie) |
| std::ios::basefield | std::ios::hex | Drukuje wartości w formacie szesnastkowym |
| std::ios::basefield | std::ios::oct | Drukuje wartości w formacie ósemkowym |
| std::ios::basefield | (none) | Drukuje wartości według wiodących znaków wartości |
| Manipulator | Znaczenie |
|---|---|
| std::dec | Drukuje wartości w decimal |
| std::hex | Drukuje wartości w formacie szesnastkowym |
| std::oct | Drukuje wartości w formacie ósemkowym |
Przykład:
std::cout << 27 << '\n';
std::cout.setf(std::ios::dec, std::ios::basefield);
std::cout << 27 << '\n';
std::cout.setf(std::ios::oct, std::ios::basefield);
std::cout << 27 << '\n';
std::cout.setf(std::ios::hex, std::ios::basefield);
std::cout << 27 << '\n';
std::cout << std::dec << 27 << '\n';
std::cout << std::oct << 27 << '\n';
std::cout << std::hex << 27 << '\n';Wynik:
27 27 33 1b 27 33 1b
W tej chwili powinieneś widzieć zależność pomiędzy ustawieniem formatowania za pomocą flagi i manipulatorów. W przyszłych przykładach będziemy używać manipulatorów, chyba że będą one niedostępne.
Precyzja, zapis i punkty dziesiętne
Za pomocą manipulatorów (lub flag) możliwa jest zmiana precyzji i formatu wyświetlania liczb zmiennoprzecinkowych. Istnieje kilka opcji formatowania, które łączą się w dość złożony sposób, więc przyjrzymy się temu bliżej.
| Grupa | Flag | Znaczenie |
|---|---|---|
| std::ios::floatfield | std::ios::fixed | Używa notacji dziesiętnej dla liczb zmiennoprzecinkowych numery |
| std::ios::floatfield | std::ios::scientific | Używa notacji naukowej dla liczb zmiennoprzecinkowych |
| std::ios::floatfield | (none) | Używa ustalonej notacji dla liczb z kilkoma cyframi, naukowe w przeciwnym razie |
| std::ios::floatfield | std::ios::showpoint | Zawsze pokazuj przecinek dziesiętny i końcowe zera dla wartości zmiennoprzecinkowych |
| Manipulator | Znaczenie |
|---|---|
| std::fixed | Użyj notacji dziesiętnej dla wartości |
| std::scientific | Użyj notacji naukowej dla wartości |
| std::showpoint | Pokaż przecinek dziesiętny i końcowe zera dla wartości zmiennoprzecinkowych |
| std::noshowpoint | Nie pokazuj przecinka dziesiętnego i końcowych 0 dla wartości zmiennoprzecinkowych |
| std::setprecision(int) | Ustawia precyzję liczb zmiennoprzecinkowych (zdefiniowanych w nagłówku iomanip) |
| Funkcja składowa | Znaczenie |
|---|---|
| std::ios_base::precision() | Zwraca bieżącą precyzję liczby zmiennoprzecinkowej numery |
| std::ios_base::precision(int) | Ustawia precyzję liczb zmiennoprzecinkowych i zwraca starą precyzję |
Jeśli używana jest notacja stała lub naukowa, precyzja określa liczbę wyświetlanych miejsc dziesiętnych ułamka. Należy pamiętać, że jeśli precyzja jest mniejsza niż liczba cyfr znaczących, liczba zostanie zaokrąglona.
std::cout << std::fixed << '\n';
std::cout << std::setprecision(3) << 123.456 << '\n';
std::cout << std::setprecision(4) << 123.456 << '\n';
std::cout << std::setprecision(5) << 123.456 << '\n';
std::cout << std::setprecision(6) << 123.456 << '\n';
std::cout << std::setprecision(7) << 123.456 << '\n';
std::cout << std::scientific << '\n';
std::cout << std::setprecision(3) << 123.456 << '\n';
std::cout << std::setprecision(4) << 123.456 << '\n';
std::cout << std::setprecision(5) << 123.456 << '\n';
std::cout << std::setprecision(6) << 123.456 << '\n';
std::cout << std::setprecision(7) << 123.456 << '\n';Daje wynik:
123.456 123.4560 123.45600 123.456000 123.4560000 1.235e+002 1.2346e+002 1.23456e+002 1.234560e+002 1.2345600e+002
Jeśli nie stosuje się wartości stałych ani naukowych, precyzja określa, ile cyfr znaczących powinno zostać wyświetlonych. Ponownie, jeśli precyzja jest mniejsza niż liczba cyfr znaczących, liczba zostanie zaokrąglona.
std::cout << std::setprecision(3) << 123.456 << '\n';
std::cout << std::setprecision(4) << 123.456 << '\n';
std::cout << std::setprecision(5) << 123.456 << '\n';
std::cout << std::setprecision(6) << 123.456 << '\n';
std::cout << std::setprecision(7) << 123.456 << '\n';Wygeneruje następujący wynik:
123 123.5 123.46 123.456 123.456
Używając manipulatora lub flagi punktu pokazowego, możesz zmusić strumień do zapisania przecinka dziesiętnego i zer końcowych.
std::cout << std::showpoint << '\n';
std::cout << std::setprecision(3) << 123.456 << '\n';
std::cout << std::setprecision(4) << 123.456 << '\n';
std::cout << std::setprecision(5) << 123.456 << '\n';
std::cout << std::setprecision(6) << 123.456 << '\n';
std::cout << std::setprecision(7) << 123.456 << '\n';Wygeneruje następujący wynik:
123. 123.5 123.46 123.456 123.4560
Oto tabela podsumowująca zawierająca więcej informacji przykłady:
| Opcja | Precyzja | 12345.0 | 0.12345 |
|---|---|---|---|
| Normalny | 3 | 1.23e+004 | 0.123 |
| 4 | 1.2 35e+004 | 0.1235 | |
| 5 | 12345 | 0.12345 | |
| 6 | 12345 | 0.12345 | |
| Sho wpoint | 3 | 1.23e+004 | 0.123 |
| 4 | 1.2 35e+004 | 0.1235 | |
| 5 | 12345. | 0.12345 | |
| 6 | 12345.0 | 0.123450 | |
| Naprawiono | 3 | 12345.000 | 0.123 |
| 4 | 12345.0000 | 0.1235 | |
| 5 | 12345.00000 | 0.12345 | |
| 6 | 12345.000000 | 0.123450 | |
| Naukowe | 3 | 1.2 35e+004 | 1.235e-001 |
| 4 | 1.2345e+004 | 1.2345e-001 | |
| 5 | 1.23450e +004 | 1.23450e-001 | |
| 6 | 1.234500e+004 | 1.234500e-001 |
Szerokość, wypełnianie znaków i justowanie
Zazwyczaj podczas drukowania liczb są one drukowane bez względu na przestrzeń wokół nich. Istnieje jednak możliwość uzasadnienia drukowania liczb w lewo lub w prawo. Aby to zrobić, musimy najpierw zdefiniować szerokość pola, która określa liczbę spacji wyjściowych, jaką będzie miała wartość. Jeśli rzeczywista wydrukowana liczba jest mniejsza niż szerokość pola, zostanie ona wyrównana do lewej lub prawej strony (zgodnie z określeniem). Jeśli rzeczywista liczba jest większa niż szerokość pola, nie zostanie obcięta - przepełni pole.
| Grupa | Flag | Znaczenie |
|---|---|---|
| std::ios::justfield | std::ios::internal | Wyrównanie do lewej znaku liczby i do prawej wartości |
| std::ios::justfield | std::ios::left | Do lewej-wyrównanie znaku i wartość |
| std::ios::justfield | std::ios::right | Do prawej-wyrównuje znak i wartość (domyślnie) |
| Manipulator | Znaczenie |
|---|---|
| std::internal | Wyrównanie do lewej znaku liczby i do prawej wartości |
| std::left | Do lewej-wyrównanie znaku i wartość |
| std::right | Wyrównuje znak i wartość do prawej |
| std::setfill(char) | Ustawia parametr jako znak wypełniający (zdefiniowany w nagłówku iomanip) |
| std::setw(int) | Ustawia szerokość pola dla wejścia i wyjścia parametr (zdefiniowany w nagłówku iomanip) |
| Funkcja składowa | Znaczenie |
|---|---|
| std::basic_ostream::fill() | Zwraca bieżący znak wypełnienia |
| std::basic_ostream::fill(char) | Ustawia znak wypełnienia i zwraca stare wypełnienie znak |
| std::ios_base::width() | Zwraca bieżącą szerokość pola |
| std::ios_base::width(int) | Ustawia bieżącą szerokość pola i zwraca starą szerokość pola |
Aby użyć któregokolwiek z tych formaterów, musimy najpierw ustawić szerokość pola. Można to zrobić za pomocą funkcji członkowskiej szerokość(int) lub manipulatora setw(). Należy pamiętać, że ustawieniem domyślnym jest wyrównanie do prawej strony.
std::cout << -12345 << '\n'; // print default value with no field width
std::cout << std::setw(10) << -12345 << '\n'; // print default with field width
std::cout << std::setw(10) << std::left << -12345 << '\n'; // print left justified
std::cout << std::setw(10) << std::right << -12345 << '\n'; // print right justified
std::cout << std::setw(10) << std::internal << -12345 << '\n'; // print internally justifiedDaje to wynik:
-12345
-12345
-12345
-12345
- 12345
Jedną rzeczą wartą odnotowania jest to, że setw() i szerokość() wpływają tylko na następną instrukcję wyjściową. Nie są one trwałe jak inne flagi/manipulatory.
Ustawmy teraz znak wypełniający i wykonajmy ten sam przykład:
std::cout.fill('*');
std::cout << -12345 << '\n'; // print default value with no field width
std::cout << std::setw(10) << -12345 << '\n'; // print default with field width
std::cout << std::setw(10) << std::left << -12345 << '\n'; // print left justified
std::cout << std::setw(10) << std::right << -12345 << '\n'; // print right justified
std::cout << std::setw(10) << std::internal << -12345 << '\n'; // print internally justifiedTo daje wynik:
-12345 ****-12345 -12345**** ****-12345 -****12345
Zauważ, że wszystkie puste miejsca w polu zostały wypełnione znakiem wypełniającym.
Klasa ostream i biblioteka iostream zawierają inne funkcje wyjściowe, flagi i manipulatory, które mogą być przydatne w zależności od tego, co musisz zrobić. Podobnie jak w przypadku klasy istream, tematy te bardziej nadają się do samouczka lub książki skupiającej się na bibliotece standardowej.

