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); // włącz flagę 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); // włącz flagę std::ios::showpos i std::ios::uppercase
std::cout << 1234567.89f << '\n';To daje:
+1.23457E+06
Aby wyłączyć flagę, użyj unsetf() funkcji:
std::cout.setf(std::ios::showpos); // włącz flagę std::ios::showpos flag
std::cout << 27 << '\n';
std::cout.unsetf(std::ios::showpos); // wyłącz flagę std::ios::showpos
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); // spróbuj włączyć wyjście szesnastkowe
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); // wyłącz wyjście dziesiętne
std::cout.setf(std::ios::hex); // włącz wyjście szesnastkowe
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:
// Włącz std::ios::hex jako jedyną flagę std::ios::basefield
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'; // drukuj 27 w szesnastkowym
std::cout << 28 << '\n'; // nadal jesteśmy hex
std::cout << std::dec << 29 << '\n'; // z powrotem do wartości dziesiętnychTen 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'; // drukuj wartość domyślną bez szerokości pola
std::cout << std::setw(10) << -12345 << '\n'; // drukuj wartość domyślną z szerokością pola
std::cout << std::setw(10) << std::left << -12345 << '\n'; // drukuj wyjustowane do lewej
std::cout << std::setw(10) << std::right << -12345 << '\n'; // drukuj wyjustowane do prawa
std::cout << std::setw(10) << std::internal << -12345 << '\n'; // drukuj wyrównane wewnętrznieDaje 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'; // drukuj wartość domyślną bez szerokości pola
std::cout << std::setw(10) << -12345 << '\n'; // drukuj wartość domyślną z szerokością pola
std::cout << std::setw(10) << std::left << -12345 << '\n'; // drukuj wyjustowane do lewej
std::cout << std::setw(10) << std::right << -12345 << '\n'; // drukuj wyjustowane do prawa
std::cout << std::setw(10) << std::internal << -12345 << '\n'; // drukuj wyrównane wewnętrznieTo 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.

