Funkcja formatująca: printf

Biblioteka standardowa C, nie jest aż tak obfita, jak na przykład ta z PHP, jednak posiada parę idealnych funkcji z których można, albo nawet trzeba, korzystać. Jedną z nich jest funkcja, albo nawet rodzina funkcji o wspólnym członie: printf. Wszystkie one mieszczą się w pliku nagłówkowym: <stdio.h>.

Działanie tych funkcji polega na zapisaniu tekstu podanego jako argument tej funkcji do pliku (fprintf), na standardowe wyjście (printf) i do c-stringa (sprintf). Działanie tej funkcji nie jest jednak tak błahe. Pozwala ona bowiem na wpisanie na standardowe wyjście tekstu ‚sformatowanego’ (btw nie wiem jak to poprawnie przetłumaczyć na polski, więc zostańmy przy tym makaronizmie). Czyli mówiąc po ludzku możemy wyświetlać wartości zmiennych w czytelnych dla ludzi formacie.

W ‚czytelnym dla ludzi formacie‚? Przecież wystarczy tylko ‚std::cout << zmienna‚. No właśnie nie wystarczy – szczególnie w C które nie posiada: klas, przeciążania operatorów i couta czy cina.

By pokazać dlaczego ta umiejętność tej funkcji jest taka ważna, przeprowadźmy test. Zapiszmy do pliku wartość zmiennej tak jak jest ona przedstawiona w pamięci:

#include <stdio.h>
int main()
{
 FILE * f = fopen("dane.txt", "wb"); //otwieramy plik
 int zmienna = 126;
 fwrite(&zmienna, sizeof(int), 1, f); //zapisujemy do pliku wartość zmiennej
 fclose(f); //zamykamy plik
 return 0;
}

I teraz zagwozdka: co będzie w pliku: dane.txt? Jeśli ktoś udzielił odpowiedzi że liczba 126 to ma częściowo rację. Będzie tam liczba 126 ale zapisana ‚tak jak jest w pamięci’, czyli: ~    (tak tylda i 3 znaki o kodzie 0 [czyli NULe]). Nie oczekujemy chyba że zwykły użytkownik będzie w stanie zrozumieć że ten ciąg niezwiązanych ze sobą cyfr/znaków/itp jest liczbą którą chcemy jemu pokazać.

Dlatego tutaj potrzebna jest konwersja z liczby całkowitej na c-stringa. Można – w tym przypadku – użyć funkcji atoi z nagłówka. Ale po co skoro mamy printf który dokona nam konwersji w locie.

Funkcja jako argument przyjmuje ciąg c-string który ustala ciąg formatujący. W tym ciągu możemy użyć znaczników formatujących które mają taki format: %[flagi][margines][precyzja][długość]specyfikator. Kolejne zaś argumenty jakie może przyjąć ta funkcja to zmienne które chcemy wyświetlić.

I tak dla przykładu by wypisać do pliku wartość tej biednej zmiennej musimy posłużyć się funkcją fprintf:

#include <stdio.h>
int main()
{
 FILE * f = fopen("dane.txt", "wb"); //otwieramy plik
 int zmienna = 126;
 fprintf(f, "%d", zmienna); //zapisujemy do pliku wartość zmiennej
 fclose(f); //zamykamy plik
 return 0;
}

W pliku będzie teraz zapisane: 126.

Te znaczniki formatowania mają jednak bardziej zaawansowaną postać niż %d. Teraz czas by wyjaśnić co można z nimi zrobić:

  • flagi
    • - (minus) – Wyrównuje tekst do lewej względem podanego marginesu. Domyślnie tekst jest wyrównywany do prawej.
    • + (plus)- Wymusza by wartość była poprzedzona znakiem + (plus) lub (-) minus, nawet dla wartości większych od zera. Domyślnie pokazywany jest tylko minus dla wartości mniejszych od zera.
    • (spacja) – Jeśli wartość do wyświetlenia jest dodatnia, w miejscu znaku zostanie wstawiona spacja.
    • # (hash)- Gdy użyte ze specyfikatorem: o, x lub X. Wyświetla przed wartością: 0, 0x lub OX, dla wartości różnych od zera.
      Gdy użyte razem ze e,E lub f, wymusza wypisanie separatora dziesiętnego. Domyślnie gdy nie ma żadnej liczby po przecinku, przecinek nie jest wyświetlany.
      Użyty z g i G daje taki sam efekt jak użyty z e i E, ale końcowe zera nie są obcinane.
    • 0 (zero) – Jeśli został zdefiniowany margines, zamiast spacji w miejsce dopełnienia są dodawane zera.
  • margines
    • Jeśli podamy jakąś liczbę – będzie to minimalna ilość znaków do wyświetlenia. Jeśli wartość zmiennej będzie mniejsza niż ilość tych znaków, brakujące znaki zostaną dopełnione spacjami. Nawet jeśli wartość zmiennej nie będzie mieścić się w podanym zakresie, wartość zmiennej nie będzie obcięta.
    • * (gwiazdka) – jeśli podamy gwiazdkę, to ilość znaków które mają stanowić margines będzie zapisane w zmiennej poprzedzającą zmienną z wartością.
  • .precyzja – nie należy zapominać o kropce!
    • Jeśli podamy jakąś liczbę – Dla liczb całkowitych (d, i, o, u, x, X) jest to jaka liczba znaków ma być wypisana minimalnie. Jeśli liczba jest zbyt mała to zostanie ona dopełniona wiodącymi zerami. Precyzja 0, oznacza że dla zera nie zostanie wyświetlony żadna wartość.
      Dla e, E i f: to jest ilość cyfr wyświetlona po przecinku
      Dla g i G: To jest maksymalna ilość wyświetlonych cyfr znaczących
      Dla s: jest to maksymalna ilość znaków które zostaną wypisane. Domyślnie wypisywane są wszystkie znaki póki nie zostanie napotkany znak NULL (czyli znak o numerze 0).
      Gdy nie zostanie zdefiniowana żadna precyzja, domyślnie brane jest 1. Gdy zostanie napotkany sam znak kropki, brana jest wtedy precyzja 0.
    • * (gwiazdka) – jeśli podamy gwiazdkę, to ilość znaków które mają stanowić margines będzie zapisane w zmiennej poprzedzającą zmienną z wartością.
  • długość
    • h (małe h) – Argument jest interpretowany jako short int lub unsigned short int (tylko dla: i, d, o, u, x i X).
    • l (małe l) – Argument jest interpretowany jako long int albo unsigned long int dla i, d, o, u, x i X, i jako szeroki znak (wide charater) albo wcstring dla c i s.
    • ll (dwa małe l) – Argument jest interpretowany jako long long int albo unsigned long long int dla i, d, o, u, x i X.
    • L (duże l) – Argument jest interpretowany jako long double, dla e, E, f, g i G.
  • specyfikator
    • c (małe c) – Pojedynczy znak
    • d albo i (małe d albo małe i) – Liczba całkowita ze znakiem
    • e (małe e) – Notacja naukowa korzystająca ze znaku e
    • E (duże e) – Notacja naukowa korzystająca ze znaku E
    • f (małe f) – liczba zmiennoprzecinkowa.
    • g (małe g) – skrócone %e lub %f
    • G (duże g) – skrócone %E lub %f
    • o (małe o) – Wyświetlenie liczby w notacji ósemkowej
    • s (małe s) – Wyświetlenie c-stringu
    • u (małe u) – Wyświetlenie liczby całkowitej bez znaku
    • x (małe x) – Wyświetlenie liczby w notacji szesnastkowej (małe litery)
    • X (duże x) – Wyświetlenie liczby w notacji szesnastkowej (duże litery)
    • p (małe p) – Wyświetlenie adresu wskaźnika
    • n (małe n) – Nic nie jest wyświelone. Ten argument musi być wskaźnikiem na inta który będzie przechowywał liczbę znaków już wyświetlonych.
    • % (procent) – Wyświetlenie znaku %

Jeśli przeczytaliście tą całą listę to gratulacja dla was, jeśli nie przeczytaliście to też należą wam się gratulacje, bo programista powinien wiedzieć gdzie znaleźć te informacje, a nie musi ich znać na pamięć.

Jednak jako że pewnie większość tekstu na górze nie przeczytała, to pokaże teraz do czego można wykorzystać znaczniki formatowania w funkcjach z rodziny printf. To co jest w komentarzach, to jest to co wypisze na standardowe wyjście funkcja:

#include <stdio.h>
int main()
{
 //zabawa z printf
 printf("Znaki: %c, %c\n", 'a', 66); //Znaki: a, B
 printf("Liczby: %+d, %3d, %03d, %d\n", 10, 10, 10, 10); //Liczby: +10,  10, 010, 10
 printf("Naukowa: %.2e, %2.3E, %#.e, %3.2E\n", 23.22f, 23.22, 23.22, 23.22); //2.32e+001, 2.322E+001, 2.e+001, 2.32E+001
 printf("Zmienno liczbowa:  %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416); //Zmienno liczbowa:  3.14 +3e+000 3.141600E+000
 printf("Podstawy: %o, %d, %x, %#o, %#d, %#x\n", 100, 100, 100, 100, 100, 100); //Podstawy: 144, 100, 64, 0144, 100, 0x64
 printf("Napisy: %s, %.3s\n", "Test", "Test"); //Napisy: Test, Tes
 printf("Margines: %*s", 30, "Nie zajme 30 znakow\n"); //Margines:           Nie zajme 30 znakow
 return 0;
}

A na koniec pojawia się pytanie, czy korzystając z C++ będziemy musieli kiedyś korzystać z tej funkcji? Bo co jak co, std::cout wydaje się wygodniejsze. Moim zdaniem – w końcu to mój blog więc o moje zdanie chodzi – dla osób piszących w C++ nie będzie ta funkcja potrzebna, jeśli chodzi o proste programy. W bardziej zaawansowanych programach, przyda się znajomość znaczników formatujących, gdy obsługa strumieni/formatowania wyjścia będzie opierać się właśnie na printf-ach a nie na strumieniach znanych z C++.

Ja zaś używam gdzie się da printf-a bo jest dla mnie czytelniejszy niż cout, szczególnie jeśli chodzi o wyświetlanie liczb zmiennoprzecinkowych.

Advertisements

About Andrzej "PsychoB" Budzanowski

Technik Informatyk, ponoć programista.

Posted on 5 listopada 2011, in C++, Programowanie, Rzeczy Oczywiste and tagged , , , , . Bookmark the permalink. 1 komentarz.

  1. dla mnie printf zawsze było wygodniejsze, znaczniki formatujące przydają mi się zawsze i wszędzie 😉

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

%d blogerów lubi to: