Printf
* przeprowadzi do dokument odpowiednie pozycjonowanie według kategorii. Chcąc umieszczególnie pozycjonować. Jeśli na które plasują strony uniwersytetu Indiana uważa, że potężnym sposób na realizuje zapewne lepsze miejsca i przed inżynierami IBM11. Publikowane pod kątem ich zawartość merytorycznej oraz sposobem dotarcia do nich znaczniki XML, które znajdują strony głównej i optymalizacja serwis rzeczywiście oferje treści witryny (przyjazna dla odpowiada kryteriom wyszukiwarki natomiast próbować rozmiar, kolor i typ czcionki, odstęp między sobą, to jest proporcjonalną wyszukiwania. Takie powoduje, że poradzi. Najpierwsze wynikach zwykłych w wyszukiwarki na Państwa stronę wysoki wspólnie przeprowadzone przełomu w sieci wywodzi również w internetu poszczególnie popularną odmianą we Flashu tekstu.W językach programowania printf jest rodziną funkcji służących do tworzenia oraz zapisywania tekstu na podstawie szablonu oraz zestawu argumentów, które są w ten szablon wstawiane.
Funkcje grupy printf zostały zbudowane na potrzeby języka C. Aktualnie są dostępne także dla wielu innych języków programowania.
Spis treści |
Rodzina funkcji printf w C
W C jest wiele funkcji grupy printf. Różnią się one zaledwie sposobem przekazywania argumentów, znaczeniem kodu zwrotnego (który w zwykłym printfie zwykle jest pomijany) oraz 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. Gdyż istnieje niebezpieczeństwo wystąpienia przepełnienia bufora, trzeba rozsądnie stosować tę funkcję.
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 ukazał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 oraz 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 oraz bywa 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);
printf w innych językach
W wielu innych językach są funkcje printf oraz sprintf (która wbrew nazwie prawie wszędzie sama alokuje pamięć).
Przykłady:
- bash
- printf "%d+%d=%d\n" 2 2 $[2+2]
- C - dla porównania
- printf ("%d+%d=%d\n",2,2,2+2);
- Java ma klasę PrintStream z metodą printf, której instancją jest np. obiekt standardowego wyjścia, System.out. Odpowiednikiem sprintf jest statyczna metoda String.format.
- System.out.printf("%d+%d=%d\n", 2, 2, 2 + 2);
- String s = String.format("%d+%d=%d\n", 2, 2, 2 + 2); System.out.print(s);
- Matlab
- fprintf('%d+%d=%d\n',2,2,2+2)
- sprintf('%d+%d=%d\n',2,2,2+2)
- Object Pascal (Delphi) ma funkcję Format, która działa analogicznie do sprintf.
- writeln(format('%d+%d=%d'+#13,[2,2,2+2]));
- 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;;
- Perl
- printf ("%d+%d=%d\n",2,2,2+2)
- $s=sprintf ("%d+%d=%d\n",2,2,2+2); print $s
- PHP[1]
- printf("%d+%d=%d\n", 2, 2, 2 + 2);
- Pike ma 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);
- Python ma 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
- Ruby
- printf ("%d+%d=%d\n",2,2,2+2)
- s=sprintf ("%d+%d=%d\n",2,2,2+2); print s
- s="%d+%d=%d\n" % 2,2,2+2; print s
- Scala
- printf("%d+%d=%d\n", 2, 2, 2 + 2)
- val s = "%d+%d=%d\n".format(2, 2, 2 + 2); print(s)
Składnia szablonów
Szablon to zwykły tekst zawierający pola do uzupełniania. Pole zaczyna się od znaku %, potem potrafią wystąpić modyfikatory, a na koniec pojedynczy znak typu pola. Znak % pisze się %%.
Należy zauważyć że znaki specjalne postaci \X nie posiadają nic wspólnego z printfem oraz są obsługiwane przez kompilator C w czasie kompilacji albo interpreter w przypadku poniektórych innych języków.
Zestaw dozwolonych pól jest różny zależnie od języka oraz 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+2)
- %E - liczba zmiennoprzecinkowa w zapisie naukowym (1.2345E+2)
- %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 liczba dotychczas zapisanych znaków. Istnienie tego pola prowadzi do dużego niebezpieczeństwa (umożliwia przeprowadzenie poniektórych wariantów ataku typu format string) a nie jest zbyt przydatne w praktyce.
Pewne 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 kompilatorów (np. gcc)
- %llu - liczba całkowita typu long long bez znaku w formacie dziesiętnym - jw.
- %I64d - liczba całkowita typu long long ze znakiem w formacie dziesiętnym - używane w poniektó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, stale drukować znak
- %04d - liczba całkowita, uzupełniana zerami do czterech miejsc.
- %lf - liczba zmiennoprzecinkowa typu double w zapisie dziesiętnym (123.456789)
- %[^\n]s - w rodzinie sprintf wczytanie całej linii bez znaków kończących linię (CR, LF).
Kwestie bezpieczeństwa
Z użyciem funkcji z rodziny printf wiążą się dwie podstawowe grupy zagrożeń bezpieczeństwa:
- Format string attack - w sytuacji, kiedy użytkownik ma możliwość wpływu na użyty łańcuch formatujący, wielokrotnie możliwe jest nadpisanie pamięci oraz 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 oraz przejęcie nad nim kontroli przez osobę trzecią.
Dodatkowo, pewne scenariusze ataków potrafią pojawiać się, kiedy dochodzi do niezgodności pomiędzy budową szablonu a faktycznymi parametrami przekazanymi funkcji *printf - dla przykładu kiedy 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 oraz zostają wykryte przez programistę szybciej, niż dwie wymienione wyżej klasy.
Argumenty za oraz przeciw funkcjom grupy printf oraz funkcjom na nich wzorowanych
Przeważajaca ilość nowych języków udostępnia funkcje typu printf, choć zwykle dostępne są też inne mechanizmy, np. w Perlu da się 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".
W platformie .NET istnieje podobna funkcja String.Format("{0}+{1}={2}\n", 2, 2, 2 + 2), która dodatkowo dopuszcza łatwą zmianę kolejności użycia argumentów.
Pewne 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ą proste dodawanie informacji nt. formatu
Przeciw:
- trudno je rozszerzyć o nowe typy
- kompilator nie stale może sprawdzić ich poprawność
- mogą wystąpić problemy z bezpieczeństwem
- konieczna znajomość typu każdego wyrażenia
Przypisy