Menu

Printf ו-Sprintf ב-Golang: דף עזר ל-verbs של fmt

איך חבילת fmt של Go מדפיסה ומעצבת ערכים: Println מול Printf מול Sprintf מול Errorf, הטבלה המלאה של format verbs (%v, %+v, %d, %s, %q, %f, %T, %w ועוד), רוחב, דיוק וריפוד.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

לחבילת fmt יש שלוש משפחות של פונקציות הדפסה, ולכל משפחה אותן שלוש גרסאות:

פונקציהלאן הפלט הולךפורמט
Print, Println, Printfהפלט הסטנדרטיברירת מחדל, ברירת מחדל עם רווחים ושורה חדשה, מחרוזת פורמט
Sprint, Sprintln, Sprintfstring מוחזראותם שלושה סגנונות
Fprint, Fprintln, Fprintfכל io.Writer (קובץ, buffer, תשובת HTTP)אותם שלושה סגנונות
Errorferror מוחזרמחרוזת פורמט, ועוד %w לעטיפה

Println מוסיפה רווחים בין האופרנדים ושורה חדשה בסוף. Printf לא מוסיפה כלום: את \n כותבים בעצמכם. Print מוסיפה רווחים רק בין אופרנדים ששניהם אינם מחרוזות, וזה מפתיע מספיק כדי שרוב הקוד ישתמש ב-Println או ב-Printf.

Format verbs

כלליים

Verbמדפיספלט לדוגמה
%vאת הערך בפורמט ברירת מחדל{Ana 31 [admin]}
%+vstructs עם שמות השדות{Name:Ana Age:31 Tags:[admin]}
%#vתחביר Go של הערךmain.User{Name:"Ana", Age:31, Tags:[]string{"admin"}}
%Tאת הטיפוסmain.User
%%סימן אחוז מילולי%

%+v הוא זה שכדאי להשתמש בו בדיבוג. מצביע ל-struct מודפס כ-&{...} ולא ככתובת. maps מודפסים עם מפתחות ממוינים, כך שהפלט יציב גם אם סדר המעבר על map אקראי.

מספרים שלמים

Verbמשמעותfmt.Sprintf(verb, 255)
%dעשרוני255
%bבינארי11111111
%oאוקטלי377
%Oאוקטלי עם הקידומת 0o0o377
%x / %Xהקסדצימלי, באותיות קטנות או גדולותff / FF
%#xהקסדצימלי עם הקידומת 0x0xff
%cהתו עם נקודת הקוד הזאתÿ
%qליטרל תו בגרשיים'ÿ'
%Uפורמט UnicodeU+00FF

מספרים עשרוניים

Verbמשמעותfmt.Sprintf(verb, 1234.5678)
%fעשרוני, 6 ספרות כברירת מחדל1234.567800
%.2fעשרוני, 2 ספרות1234.57
%eכתיב מדעי1.234568e+03
%g%e או %f, הקצר מביניהם, בלי אפסים נגררים1234.5678
%vכמו %g1234.5678

%.2f מעגל את הערך הבינארי המדויק של ה-float, ולכן fmt.Sprintf("%.2f", 2.675) הוא 2.67: ה-float64 הקרוב ביותר ל-2.675 קטן ממנו במעט. אף פעם אל תעצבו כסף מ-float; שמרו אגורות במספר שלם.

מחרוזות ובתים

Verbמשמעותfmt.Sprintf(verb, "go\n")
%sהמחרוזת כמו שהיאgo ושורה חדשה
%qבמירכאות כפולות, עם תווי escape גלויים"go\n"
%xהקסדצימלי של כל בית676f0a
% xהקסדצימלי עם רווחים67 6f 0a

%s על []byte מדפיס אותו כטקסט; %v מדפיס את המספרים ([104 105]).

טיפוסים אחרים

Verbטיפוסמדפיס
%tbooltrue או false
%pמצביע, slice, map, channel, פונקציהאת הכתובת, כמו 0xc000012345
%werror (רק ב-Errorf)את ההודעה של השגיאה, ועוטף אותה

רוחב, דיוק וריפוד

בין % ל-verb אפשר לשים דגלים, רוחב ודיוק:

צורההשפעה
%5dרוחב 5, מיושר לסוף השדה (ריפוד ברווחים לפני הערך)
%-5dרוחב 5, מיושר לתחילת השדה
%05dריפוד באפסים
%.2f2 ספרות אחרי הנקודה העשרונית
%8.2fרוחב 8 ו-2 ספרות אחרי הנקודה
%.3sלכל היותר 3 תווים מהמחרוזת
%+dתמיד להציג את הסימן
%*dהרוחב נלקח מהארגומנט הבא

שימו לב ש-%.0f של 2.5 מדפיס 2: כאן Go מעגלת חצי למספר הזוגי. במחרוזות, הרוחב סופר runes ולא עמודות תצוגה, כך שתווי CJK ואימוג'י עדיין יכולים לשבש את היישור של טבלה. לעמודות מיושרות של טקסט באורך משתנה, text/tabwriter עושה את המדידה בשבילכם.

אינדקסים של ארגומנטים

%[n] בוחר ארגומנט לפי המיקום שלו, וזה מאפשר להשתמש באחד שוב:

fmt.Printf("%[2]s %[1]s\n", "world", "hello") // hello world
fmt.Printf("%d %[1]x %[1]b\n", 10)             // 10 a 1010

Errorf ו-%w

fmt.Errorf בונה error ממחרוזת פורמט. עם %w היא גם עוטפת שגיאה אחרת, כך שמי שקורא עדיין יכול לזהות את המקורית:

השתמשו ב-%w כשמי שקורא עשוי להצטרך לבדוק את הסיבה, וב-%v כשאתם מסתירים אותה בכוונה. החל מ-Go 1.20 קריאה אחת ל-Errorf יכולה להכיל כמה verbs של %w. הדף על טיפול בשגיאות מסביר עטיפה לעומק.

עיצוב מותאם עם String()

כל טיפוס עם מתודת String() string קובע איך %v, %s ו-Println מציגים אותו:

%d עוקף את String() ומדפיס את המספר שמתחת. עבור טיפוסי שגיאה המתודה המקבילה היא Error() string, והיא קודמת ל-String().

כשה-verb שגוי

fmt אף פעם לא נכנסת ל-panic בגלל פורמט שגוי. היא מדפיסה את הבעיה בתוך הפלט:

fmt.Printf("%d\n", "oops")
fmt.Printf("%d %d\n", 1)
fmt.Printf("%d\n", 1, 2)
%!d(string=oops)
1 %!d(MISSING)
1
%!(EXTRA int=2)

פלט כזה נוטה להגיע ל-production, כי הוא לא מפיל כלום. go vet תופס את שלושת המקרים בזמן הבנייה:

./main.go:8:2: fmt.Printf format %d has arg "oops" of wrong type string
./main.go:9:2: fmt.Printf format %d reads arg #2, but call has 1 arg
./main.go:10:2: fmt.Printf call needs 1 arg but has 2 args

הערות על ביצועים

fmt מקבלת כל ארגומנט כ-any ובוחנת את הטיפוס שלו בזמן ריצה (ונופלת חזרה ל-reflection עבור structs, slices ו-maps), וזה בסדר ללוגים ולפלט אבל מורגש בלולאות צפופות. להמרת מספר בודד, strconv.Itoa ו-strconv.FormatFloat מהירות יותר מ-Sprintf. לבניית מחרוזת ארוכה בלולאה, כתבו לתוך strings.Builder עם fmt.Fprintf(&b, ...) במקום לשרשר תוצאות של Sprintf.

שאלות נפוצות

מה ההבדל בין Println, Printf ו-Sprintf ב-Go?

fmt.Println מדפיסה את הארגומנטים שלה מופרדים ברווחים, עם שורה חדשה בסוף. fmt.Printf מדפיסה לפי מחרוזת פורמט ולא מוסיפה שורה חדשה. fmt.Sprintf מעצבת באותו אופן כמו Printf אבל מחזירה את התוצאה כמחרוזת במקום להדפיס אותה. fmt.Errorf עושה אותו דבר ומחזירה error.

איך מדפיסים struct עם שמות השדות ב-Go?

השתמשו ב-%+v: fmt.Printf("%+v\n", user) מדפיס {Name:Ana Age:31}. %v מדפיס רק את הערכים, {Ana 31}, ו-%#v מדפיס תחביר Go כולל הטיפוס, main.User{Name:"Ana", Age:31}.

איך מעצבים float לשתי ספרות אחרי הנקודה ב-Go?

השתמשו ב-%.2f: fmt.Sprintf("%.2f", 3.14159) מחזירה "3.14". הוסיפו רוחב כדי ליישר עמודות, %8.2f, או סימן מינוס כדי ליישר לתחילת השדה, %-8.2f. strconv.FormatFloat(f, 'f', 2, 64) נותנת את אותה תוצאה בלי מחרוזת פורמט.

מה עושה %w ב-fmt.Errorf?

%w מעצב שגיאה כמו %v וגם עוטף אותה, כך שהשגיאה החדשה נושאת את המקורית. errors.Is ו-errors.As יכולות אז למצוא את השגיאה העטופה: err := fmt.Errorf("load config: %w", os.ErrNotExist) גורם ל-errors.Is(err, os.ErrNotExist) להיות true. %w עובד רק ב-fmt.Errorf.

למה בפלט שלי מופיע %!d(string=...)?

ה-verb לא מתאים לטיפוס של הארגומנט, למשל %d שקיבל מחרוזת. fmt מדפיסה את הבעיה בתוך הפלט במקום להיכנס ל-panic: %!d(string=oops). ארגומנטים חסרים מודפסים כ-%!d(MISSING) ועודפים כ-%!(EXTRA int=2). go vet תופס את שלושת המקרים עוד לפני שמריצים את התוכנית.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל