פורמט מחרוזת printf היא פרמטר בקרה המשמש מחלקה של פונקציות בספריות הקלט/פלט של C ושפות תכנות רבות אחרות. המחרוזת כתובה בשפת תבנית פשוטה: תווים בדרך כלל מועתקים כמות שהם לתוך הפלט של הפונקציה, אך מפרטי פורמט, שמתחילים בתו %, מציינים את המיקום והשיטה לתרגום נתון (כגון מספר) לתווים.
"printf" הוא השם של אחת מפונקציות הפלט העיקריות של C, והוא קיצור של "print formatted". מחרוזות בפורמט printf משלימות מחרוזות בפורמט scanf, המספקות קלט מעוצב. בשני המקרים הפונקציות מספקות פונקציונליות פשוטה ופורמט קבוע בהשוואה למעבדי תבניות מתוחכמים וגמישים יותר או מנתחים, אך מספיקים למטרות רבות.
שפות רבות מלבד C מעתיקות את תחביר המחרוזת בפורמט printf, בקירוב או בדיוק, בפונקציות קלט/פלט משלהן.
חוסר התאמה בין מפרטי הפורמט וסוג הנתונים עלול לגרום לקריסות ולפגיעויות אחרות. מחרוזת הפורמט עצמה היא לעיתים קרובות מאוד מחרוזת מילולית, המאפשרת ניתוח סטטי של קריאת הפונקציה. עם זאת, זה יכול להיות גם ערך של משתנה, מה שמאפשר פורמט דינמי אך גם פגיעות אבטחה המכונה ניצול מחרוזת פורמט בלתי מבוקר .
שפות תכנות מוקדמות כמו Fortran השתמשו בהצהרות מיוחדות עם תחביר שונה לחלוטין מחישובים אחרים כדי לבנות תיאורי פורמט. בדוגמה זו, הפורמט מצוין בשורה 601, והפקודה WRITE מתייחסת אליו לפי מספר שורה:
WRITE OUTPUT TAPE 6, 601, IA, IB, IC, AREA
601 FORMAT (4H A= ,I5,5H B= ,I5,5H C= ,I5,
& 8H AREA= ,F10.2, 13H SQUARE UNITS)
ל-ALGOL 68 היה ממשק API יותר דמוי פונקציה, אבל עדיין השתמש בתחביר מיוחד (מפרידי $
מקיפים תחביר פורמט מיוחד):
printf(($"Color "g", number1 "6d,", number2 "4zd,", hex "16r2d,", float "-d.2d,", unsigned value"-3d"."l$,
"red", 123456, 89, BIN 255, 3.14, 250));
אבל השימוש בקריאות הפונקציות ובסוגי הנתונים הרגילים מפשט את השפה והקומפיילר, ומאפשר לכתוב את המימוש של הקלט/פלט באותה שפה. יתרונות אלו עולים על החסרונות (כגון חוסר מוחלט של בטיחות טיפוס במקרים רבים) וברוב השפות החדשות יותר קלט/פלט אינו חלק מהתחביר.
מקורו של printf
של C בפונקציית writef
של BCPL (1966). בהשוואה ל- C
ו- printf
, *N
הוא רצף מילוט בשפת BCPL המייצג תו שורה חדשה (שעבורו C משתמש ברצף המילוט \n
) והסדר של שדה הרוחב ושדה הטיפוס הפוכים ב-writef: [1]
WRITEF("%I2-QUEENS PROBLEM HAS %I5 SOLUTIONS*N", NUMQUEENS, COUNT)
ככל הנראה ההעתקה הראשונה של התחביר מחוץ לשפת C הייתה פקודת ה-Unix printf
shell, שהופיעה לראשונה בגרסה 4, כחלק מההיסב ל-C.
יצירת הפורמט מתבצעת באמצעות שומרי מקום בתוך מחרוזת הפורמט. לדוגמה, אם תוכנית אמורה להדפיס את גילו של אדם, היא יכולה להציג את הפלט על ידי הקדמת "הגיל שלך הוא ", ושימוש בתו המציין מספר עשרוני שלם - d
כדי לציין שאנו רוצים שהמספר השלם של הגיל יוצג מיד לאחר הודעה זו, אנו עשויים להשתמש במחרוזת הפורמט:
printf("Your age is %d", age);
התחביר עבור שומר מקום של פורמט הוא
זוהי הרחבה של POSIX ולא כלולה בתקן C99 של שפת C. השימוש בשדה הוא על ידי הוספת פרמטר מספרי ולאחריו התו $ בין התו המיוחד % ובין התו שמסמן את הטיפוס, למשל %2$d מסמן שימוש בערך השני מבין הערכים שלאחר מחרוזת הבקרה. ניתן להשמיט את השדה, בתנאי שאף אחד משומרי המקום האחרים לא עושה בו שימוש.
תכונה זו בשימוש בעיקר בלוקליזציה, שבה סדר הופעתם של פרמטרים משתנה בהתאם לקונבנציה התלויה בשפה.
במערכת ההפעלה Microsoft Windows, התמיכה בתכונה זו ממומשת בפונקציה נפרדת, printf_p.
שדה הדגלים יכול להיות אפס או יותר (בכל סדר) מהתווים הבאים:
שדה הרוחב מציין מספר מינימלי של תווים לפלט, והוא משמש בדרך כלל לרפד שדות ברוחב קבוע בפלט עבור טבלאות, שם העמודות יהיו צרות יותר אילולי הריפוד. השדה אינו גורם לקיצוץ של שדות גדולים מרוחב השדה שצוין.
ניתן להשמיט את שדה הרוחב, או לספק בו ערך מספרי שלם, או ערך דינמי אשר מועבר כארגומנט נוסף, כאשר מסומן באמצעות כוכבית. [2] לדוגמה, printf("%*d", 5, 10)
יביא להדפסה של 10
, ברוחב כולל של 5 תווים.
אף על פי שאינו חלק משדה הרוחב, אפס מוביל מתפרש כדגל ריפוד האפס שהוזכר לעיל, וערך שלילי מטופל כערך החיובי יחד עם היישור לשמאל שהוזכר גם כן למעלה.
שדה הדיוק מציין בדרך כלל מגבלת מקסימום על הפלט, בהתאם לטיפוס המסוים. עבור טיפוסים מסוג נקודה צפה, הוא מציין את מספר הספרות מימין לנקודה העשרונית אליו יש לעגל. עבור טיפוס המחרוזת, הוא מגביל את מספר התווים שיש להדפיס, ולאחר מכן המחרוזת נחתכת.
ניתן להשמיט את שדה הדיוק, או להזין ערך מספרי שלם, או ערך דינמי אשר מועבר כארגומנט נוסף כאשר מסומן באמצעות כוכבית. לדוגמה, printf("%.*s", 3, "abcdef")
יודפס כ- abc
.
ניתן להשמיט את שדה האורך או להזין אחד מהערכים הבאים:
בנוסף, התקיימו מספר אפשרויות אורך ספציפיות לפלטפורמה לפני השימוש הנרחב בתוספי ISO C99:
בתקן ISO C99 נכלל קובץ הדר inttypes.h שכולל מספר מאקרואים לשימוש ב-printf באופן שאינו תלוי במערכת ההפעלה.
שדה הטיפוס יכול להיות אחד מהבאים:
יש כמה מימושים של פונקציות דמויות printf
המאפשרות הרחבות למיני-שפה המבוססת על תו מילוט, ובכך מאפשרות למתכנת פונקציית פירמוט ספציפית עבור טיפוסים שאינם מובנים בשפה. אחד המוכרים ביותר הוא register_printf_function()
של glibc (שהוצא משימוש). עם זאת, הוא משמש לעיתים רחוקות בשל העובדה שהוא מתנגש עם בדיקה סטטית של מחרוזות פורמט. מימוש נוסף הוא Vstr, המאפשר הוספת שמות פורמטים מרובי תווים.
יישומים מסוימים (כמו Apache HTTP Server) כוללים פונקציה משלהם דמוית printf
, ומטמיעים בתוכו הרחבות. עם זאת, לכל אלה נוטות להיות אותן בעיות שיש ל- register_printf_function()
.
פונקציית Linux kernel, printk
תומכת במספר דרכים להצגת מבני ליבה באמצעות המפרט הגנרי %p
, על ידי הוספת תווי פורמט נוספים.[3] לדוגמה, %pI4
מדפיס כתובת IPv4 בצורה מנוקדת-עשרונית. זה מאפשר בדיקה סטטית של מחרוזת פורמט (של החלק %p
) על חשבון תאימות מלאה עם printf רגיל.
רוב השפות שיש להן פונקציה דמוית printf
עוקפות את היעדר תכונה זו על ידי שימוש בפורמט %s
והמרת האובייקט לייצוג מחרוזת.
אם יש מעט מדי ארגומנטים שסופקו לספק ערכים עבור כל מפרטי ההמרה במחרוזת התבנית, או אם הארגומנטים אינם מהסוגים הנכונים, התוצאות אינן מוגדרות, ועלולות להקריס את התוכנית. המימושים אינם עקביים לגבי האם שגיאות תחביר במחרוזת צורכות ארגומנט ואיזה סוג ארגומנט הם צורכים. ארגומנטים עודפים הם חסרי השפעה. במספר מקרים, ההתנהגות הלא מוגדרת הובילה לפרצות אבטחה של "מתקפת מחרוזת פורמט". ברוב מוסכמות הקריאה של C או C++ ניתן להעביר ארגומנטים על המחסנית, מה שאומר שבמקרה של מעט מדי ארגומנטים printf יקרא מעבר לסוף מסגרת המחסנית הנוכחית, ובכך יאפשר לתוקף לקרוא את המחסנית.
כמה מהדרים, כמו GNU Compiler Collection, יבדקו באופן סטטי את מחרוזות הפורמט של פונקציות דמויות printf ויזהירו על בעיות (בעת שימוש בדגלים -Wall
או -Wformat
). GCC גם יזהיר על פונקציות בסגנון printf המוגדרות על ידי המשתמש אם ה"פורמט" הלא סטנדרטי __attribute__
מוחל על הפונקציה.
שימוש רק ברוחב שדות כדי לספק טבלאות, כמו בפורמט כמו %8d%8d%8d
עבור שלושה מספרים שלמים בשלוש עמודות של 8 תווים, לא יבטיח שהפרדת השדות תישמר אם יופיעו מספרים גדולים בנתונים:
1234567 1234567 1234567 123 123 123 123 12345678123
אובדן הפרדת שדות יכול בקלות להוביל לפלט פגום. במערכות המעודדות שימוש בתוכניות כאבני בניין בסקריפטים, ניתן להעביר נתונים פגומים כאלה ולפגוע בעיבוד נוסף שלהם, בלי להתחשב בעובדה שהמתכנת המקורי ציפה שהפלט ייקרא רק על ידי עיניים אנושיות. ניתן למנוע בעיות אלה על ידי הכללת תוחמים מפורשים, אפילו רווחים, בכל פורמטי הפלט הטבלאי. שינוי פשוט של הדוגמה המסוכנת הנזכרת ל- %7d %7d %7d
מונע ממספרים מעמודות שונות להתמזג בפלט, עקב הרווחים הכלולים במפורש:
1234567 1234567 1234567 123 123 123 123 12345678 123
אסטרטגיות דומות מיושמות על נתוני מחרוזות.
למרות שעל פני השטח זוהי פונקציית פלט, printf
מאפשרת כתיבה למיקום זיכרון שצוין על ידי ארגומנט דרך %n
. פונקציונליות זו משמשת מדי פעם כחלק מהתקפות מחרוזות פורמט משוכללות יותר.
הפונקציונליות %n
גם הופכת את printf
בטעות לשלמה טיורינג אפילו עם קבוצה מעוצבת היטב של ארגומנטים. משחק של איקס עיגול כתוב במחרוזת הפורמט הוא מנצח של תחרות IOCCC ה-27.[4]
שפות המשתמשות במחרוזות פורמט החורגות מהסגנון במאמר זה (כגון AMPL ו-Elixir), שפות שיורשות את היישום שלהן מה-JVM או סביבה אחרת (כגון Clojure ו- Scala ), ושפות שאין להן printf נייטיבי סטנדרטי אך יש להן ספריות חיצוניות המדמות התנהגות printf (כגון JavaScript) אינן כלולות ברשימה זו.
%
) [7]printf
, sprintf
ו-fmt
)print()
ו- FileStream.printf()
)std::fprintf
Formatter
ב-Java 1.5printf(1)
{{cite web}}
: (עזרה)
{{cite web}}
: (עזרה)