Printf
Z Wikipedii
- Właściwy tytuł tego artykułu to printf. Z powodu ograniczeń technicznych tytuł tego artykułu jest nieprawidłowy.
W językach programowania printf jest rodziną funkcji służących do tworzenia i zapisywania tekstu na podstawie szablonu i zestawu argumentów, które są w ten szablon wstawiane.
Funkcje grupy printf zostały stworzone na potrzeby języka C. Obecnie są dostępne również dla wielu innych języków programowania.
Spis treści |
[edytuj] Rodzina funkcji printf w C
W C występuje wiele funkcji grupy printf. Różnią się one jedynie sposobem przekazywania argumentów, znaczeniem kodu zwrotnego (który w zwykłym printfie zwykle jest pomijany) i tym, co robi się z wygenerowanym tekstem.
Funkcje, które pojawiły się we wczesnych Uniksach:
- int printf (const char *format, ...); - wygenerowany tekst pisany jest na standardowe wyjście.
- int fprintf (FILE *stream, const char *format, ...); - wygenerowany tekst pisany jest do strumienia stream
- int sprintf(char *str, const char *format, ...); - wygenerowany tekst jest zapisywany do bufora str. Funkcji tej nie należy stosować ze względu na niebezpieczeństwo wystąpienia przepełnienie bufora.
W BSD 2.11 pojawiły się wersje dla argumentów przekazywanych przez stdarg:
- int vprintf(const char *format, va_list ap);
- int vfprintf(FILE *stream, const char *format, va_list ap);
- int vsprintf(char *str, const char *format, va_list ap);
W BSD 4.4 pojawiła się funkcja :
- int snprintf(char *str, size_t size, const char *format, ...); - to samo co sprintf, tylko że zapisuje co najwyżej size znaków (wliczając w to końcowe \0), dzięki czemu jest o wiele bezpieczniejsza.
- oraz jej dpowiednik dla stdarg: int vsnprintf(char *str, size_t size, const char *format, va_list ap);
W FreeBSD i GNU libc są obecne też (niestandardowe)
- int asprintf(char **strp, const char *fmt, ...);
- int vasprintf(char **strp, const char *fmt, va_list ap);
które same alokują pamięć pod tekst. Jest to najbezpieczniejsza funkcja printf pisząca do pamięci, jednak nie jest standardowa i może być trochę wolniejsza.
W GNU libc dodatkowo są też obecne:
- int dprintf(int fd, const char *format, ...);
- int vdprintf(int fd, const char *format, va_list ap);
piszące do deskryptora zamiast do strumienia.
Istnieją też odpowiedniki dla szerokich znaków:
- int wprintf(const wchar_t *format, ...);
- int fwprintf(FILE *stream, const wchar_t *format, ...);
- int swprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, ...);
- int vwprintf(const wchar_t *format, va_list args);
- int vfwprintf(FILE *stream, const wchar_t *format, va_list args);
- int vswprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, va_list args);
[edytuj] Printf w innych językach
W wielu innych językach występują funkcje printf i sprintf (która wbrew nazwie prawie wszędzie sama alokuje pamięć).
Przykłady:
- C - dla porównania
- printf ("%d+%d=%d\n",2,2,2+2);
- Perl
- printf ("%d+%d=%d\n",2,2,2+2)
- $s=sprintf ("%d+%d=%d\n",2,2,2+2); print $s
- Powłoka
- printf "%d+%d=%d\n" 2 2 $[2+2]
- Ruby
- printf ("%d+%d=%d\n",2,2,2+2)
- s=sprintf ("%d+%d=%d\n",2,2,2+2); print s
- Python posiada operator %, z lewej strony którego jest format a z prawej lista argumentów
- print "%d+%d=%d\n"%(2,2,2+2)
- s = "%d+%d=%d\n"%(2,2,2+2); print s
- Pike posiada funkcje write o funkcjonalności analogicznej do printf. Funkcja alokująca zmienną tekstową jest jednak nazwana sprintf:
- write ("%d+%d=%d\n", 2,2,2+2);
- s = sprintf ("%d+%d=%d\n", 2,2,2+2); write (s);
- Ocaml
- open Printf;; printf "%d+%d=%d\n" 2 2 (2+2);;
- open Printf;; let a = sprintf "%d+%d=%d\n" 2 2 (2+2);; printf "%s" a;;
- Object Pascal (Delphi) posiada funkcję Format, która działa podobnie do sprintf.
- writeln(format('%d+%d=%d'+#13,[2,2,2+2]));
[edytuj] Printf w Uniksie
Systemy uniksowe mają zaimplementowaną funkcję printf jako narzędzie konsolowe.
[edytuj] Przykład
$ printf "%d+%d=%d\n" "2" "2" "2+2"
[edytuj] Składnia szablonów
Szablon to zwykły tekst zawierający pola do uzupełniania. Pole zaczyna się od znaku %, potem mogą wystąpić modyfikatory, a na koniec pojedynczy znak typu pola. Znak % pisze się %%.
Należy zauważyć że znaki specjalne postaci \X nie mają nic wspólnego z printfem i są obsługiwane przez kompilator C w czasie kompilacji lub interpreter w przypadku niektórych innych języków.
Zestaw dozwolonych pól jest różny zależnie od języka i standardu. Krótki przegląd częściej stosowanych pól:
- %d - liczba całkowita ze znakiem w formacie dziesiętnym
- %i - synonim dla %d
- %x - liczba całkowita bez znaku w formacie szesnastkowym, z użyciem małych liter
- %X - liczba całkowita bez znaku w formacie szesnastkowym, z użyciem wielkich liter
- %o - liczba całkowita bez znaku w formacie oktalnym
- %u - liczba całkowita bez znaku w formacie dziesiętnym
- %e - liczba zmiennoprzecinkowa w zapisie naukowym (1.2345e+3)
- %E - liczba zmiennoprzecinkowa w zapisie naukowym (1.2345E+3)
- %f - liczba zmiennoprzecinkowa typu double (float jest automatycznie konwertowany) w zapisie dziesiętnym (123.45)
- %c - liczba całkowita jest konwertowana na bajt o danej wartości
- %s - łańcuch tekstowy
- %p - wskaźnik
- %n - do argumentu zapisywana jest ilość dotychczas zapisanych znaków. Istnienie tego pola prowadzi do dużego niebezpieczeństwa (umożliwia przeprowadzenie niektórych wariantów ataku typu format string) a nie jest zbyt przydatne w praktyce.
Niektóre pola z modyfikatorami:
- %ld - liczba całkowita typu long ze znakiem w formacie dziesiętnym
- %lld - liczba całkowita typu long long ze znakiem w formacie dziesiętnym w większości popularnych kompilatorach (np. gcc)
- %I64d - liczba całkowita typu long long ze znakiem w formacie dziesiętnym używane w niektórych kompilatorach (np. Dev-cpp)
- %zd - liczba całkowita typu size_t ze znakiem w formacie dziesiętnym
- %hd - liczba całkowita typu short ze znakiem w formacie dziesiętnym
- % d - liczba całkowita ze znakiem w formacie dziesiętnym, w przypadku liczby dodatniej przed liczbą dać spację
- %+d - liczba całkowita typu short ze znakiem w formacie dziesiętnym, zawsze drukować znak
- %04d - liczba całkowita, uzupełniana zerami do czterech miejsc.
- %[^\n]s - w rodzinie sprintf wczytanie całej linii bez znaków kończących linię (CR, LF).
[edytuj] Kwestie bezpieczeństwa
Z użyciem funkcji z rodziny printf wiążą się dwie podstawowe grupy zagrożeń bezpieczeństwa:
- Format string attack - w sytuacji, gdy użytkownik ma możliwość wpływu na użyty łańcuch formatujący, często możliwe jest nadpisanie pamięci i przejęcie kontroli nad procesem z wykorzystaniem funkcjonalności %n. Najczęstszą przyczyną takiej podatności jest skorzystanie przez programistę z konstrukcji printf(zmienna); zamiast printf("%s",zmienna);.
- Przepełnienie bufora - funkcja *sprintf (a także nieprawidłowo użyta *snprintf) może przepełnić bufor przeznaczony przez programistę na zachowanie wyniku tej operacji, tym samym prowadząc do nadpisania struktur sterujących w pamięci procesu, i przejęcie nad nim kontroli przez osobę trzecią.
Dodatkowo, pewne scenariusze ataków mogą pojawiać się, gdy dochodzi do niezgodności między budową szablonu a faktycznymi parametrami przekazanymi funkcji *printf - na przykład gdy parametrów jest mniej niż pól w szablonie, albo wartości liczbowe przekazywane są w miejsce wskaźników. Błędy tego typu zwykle prowadzą jednak do nieprawidłowego działania programu i zostają wykryte przez programistę szybciej, niż dwie wymienione wyżej klasy.
[edytuj] Argumenty za i przeciw funkcjom grupy printf oraz funkcjom na nich wzorowanych
Większość nowych języków udostępnia funkcje typu printf, choć zwykle dostępne są też inne mechanizmy, np. w Perlu można wpisywać bezpośrednio w łańcuch tekstowy nazwy zmiennych - np. $x=2;$y=2;$z=2+2; print "$x+$y=$z", a w Ruby nawet całe wyrażenia - print "#{2}+#{2}=#{2+2}\n".
Niektóre języki, np. Ada, celowo nie posiadają funkcji tego typu. Są one tu praktycznie niemożliwe do implementacji ze względu na zbyt silną kontrolę systemu typów.
Argumenty za:
- są bardzo wygodne
- cały szablon jest w jednym miejscu
- umożliwiają łatwe dodawanie informacji nt. formatu
Przeciw:
- trudno je rozszerzyć o nowe typy
- kompilator nie zawsze może sprawdzić ich poprawności
- mogą wystąpić problemy z bezpieczeństwem
- konieczna znajomość typu każdego wyrażenia