28.3 — Wyjście z ostream i ios

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 decimal

Ten 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.

GrupaFlagZnaczenie
std::ios::boolalphaJeśli ustawione, wartości logiczne wyświetlają wartość „prawda” lub „fałsz”. Jeśli nie ustawiono, wartości logiczne wypisują 0 lub 1

ManipulatorZnaczenie
std::boolalphaBooleany wypisują „prawda” lub „fałsz”
std::noboolalphaBoolean 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

GrupaFlagZnaczenie
std::ios::showposJeśli ustawione, liczby dodatnie poprzedź znakiem +

ManipulatorZnaczenie
std::showposPoprzedza liczby dodatnie +
std::noshowposNie 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

GrupaFlagZnaczenie
std::ios::uppercaseJeśli ustawione, używa wielkich liter litery

ManipulatorZnaczenie
std::uppercaseUżywa wielkich liter
std::nouppercaseUż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

GrupaFlagZnaczenie
std::ios::basefieldstd::ios::decDrukuje wartości w formacie dziesiętnym (domyślnie)
std::ios::basefieldstd::ios::hexDrukuje wartości w formacie szesnastkowym
std::ios::basefieldstd::ios::octDrukuje wartości w formacie ósemkowym
std::ios::basefield(none)Drukuje wartości według wiodących znaków wartości

ManipulatorZnaczenie
std::decDrukuje wartości w decimal
std::hexDrukuje wartości w formacie szesnastkowym
std::octDrukuje 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.

GrupaFlagZnaczenie
std::ios::floatfieldstd::ios::fixedUżywa notacji dziesiętnej dla liczb zmiennoprzecinkowych numery
std::ios::floatfieldstd::ios::scientificUż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::floatfieldstd::ios::showpointZawsze pokazuj przecinek dziesiętny i końcowe zera dla wartości zmiennoprzecinkowych

ManipulatorZnaczenie
std::fixedUżyj notacji dziesiętnej dla wartości
std::scientificUżyj notacji naukowej dla wartości
std::showpointPokaż przecinek dziesiętny i końcowe zera dla wartości zmiennoprzecinkowych
std::noshowpointNie 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ładowaZnaczenie
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:

OpcjaPrecyzja12345.00.12345
Normalny31.23e+0040.123
41.2 35e+0040.1235
5123450.12345
6123450.12345
Sho wpoint31.23e+0040.123
41.2 35e+0040.1235
512345.0.12345
612345.00.123450
Naprawiono312345.0000.123
412345.00000.1235
512345.000000.12345
612345.0000000.123450
Naukowe31.2 35e+0041.235e-001
41.2345e+0041.2345e-001
51.23450e +0041.23450e-001
61.234500e+0041.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.

GrupaFlagZnaczenie
std::ios::justfieldstd::ios::internalWyrównanie do lewej znaku liczby i do prawej wartości
std::ios::justfieldstd::ios::leftDo lewej-wyrównanie znaku i wartość
std::ios::justfieldstd::ios::rightDo prawej-wyrównuje znak i wartość (domyślnie)

ManipulatorZnaczenie
std::internalWyrównanie do lewej znaku liczby i do prawej wartości
std::leftDo lewej-wyrównanie znaku i wartość
std::rightWyró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ładowaZnaczenie
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 justified

Daje 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 justified

To 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.

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
95 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze