للحزمة fmt ثلاث عائلات من دوال الطباعة، ولكل عائلة الأشكال الثلاثة نفسها:
| الدالة | وجهة المخرجات | التنسيق |
|---|---|---|
Print، Println، Printf | المخرج القياسي | افتراضي، افتراضي بمسافات وسطر جديد، نص تنسيق |
Sprint، Sprintln، Sprintf | string معاد | الأساليب الثلاثة نفسها |
Fprint، Fprintln، Fprintf | أي io.Writer (ملف، مخزن مؤقّت، ردّ HTTP) | الأساليب الثلاثة نفسها |
Errorf | error معاد | نص تنسيق، مع %w للتغليف |
تضيف Println مسافات بين المعاملات وسطرًا جديدًا في النهاية. أما Printf فلا تضيف شيئًا: تكتب \n بنفسك. وتضيف Print مسافات فقط بين معاملين ليس أي منهما نصًا، وهذا مفاجئ بما يكفي لتستخدم معظم الشيفرات Println أو Printf.
أفعال التنسيق
عامة
| الفعل | ما يطبعه | مثال على المخرجات |
|---|---|---|
%v | القيمة بتنسيق افتراضي | {Ana 31 [admin]} |
%+v | البنى مع أسماء الحقول | {Name:Ana Age:31 Tags:[admin]} |
%#v | صيغة Go للقيمة | main.User{Name:"Ana", Age:31, Tags:[]string{"admin"}} |
%T | النوع | main.User |
%% | علامة نسبة مئوية حرفية | % |
%+v هو ما تلجأ إليه عند تتبّع الأخطاء. المؤشر إلى بنية يُطبع بالشكل &{...} لا كعنوان. وتُطبع الخرائط بمفاتيح مرتّبة، فتكون المخرجات ثابتة رغم أن ترتيب المرور على الخريطة عشوائي.
الأعداد الصحيحة
| الفعل | المعنى | fmt.Sprintf(verb, 255) |
|---|---|---|
%d | عشري | 255 |
%b | ثنائي | 11111111 |
%o | ثماني | 377 |
%O | ثماني مع البادئة 0o | 0o377 |
%x / %X | ست عشري بأحرف صغيرة أو كبيرة | ff / FF |
%#x | ست عشري مع البادئة 0x | 0xff |
%c | الحرف صاحب نقطة الرمز تلك | ÿ |
%q | حرفية حرف بين علامتي اقتباس | 'ÿ' |
%U | صيغة Unicode | U+00FF |
الأعداد العشرية
| الفعل | المعنى | fmt.Sprintf(verb, 1234.5678) |
|---|---|---|
%f | عشري، 6 منازل افتراضيًا | 1234.567800 |
%.2f | عشري، منزلتان | 1234.57 |
%e | الصيغة العلمية | 1.234568e+03 |
%g | %e أو %f، أيهما أقصر، دون أصفار زائدة في النهاية | 1234.5678 |
%v | مثل %g | 1234.5678 |
يقرّب %.2f القيمة الثنائية الدقيقة للعدد العشري، فتكون fmt.Sprintf("%.2f", 2.675) مساوية لـ 2.67: أقرب float64 إلى 2.675 أقل منه بقليل. لا تنسّق المال من عدد عشري أبدًا؛ احتفظ بالسنتات في عدد صحيح.
النصوص والبايتات
| الفعل | المعنى | fmt.Sprintf(verb, "go\n") |
|---|---|---|
%s | النص كما هو | go ثم سطر جديد |
%q | بين علامتي اقتباس مزدوجتين مع إظهار رموز الهروب | "go\n" |
%x | ست عشري لكل بايت | 676f0a |
% x | ست عشري مع مسافات | 67 6f 0a |
يطبع %s على []byte محتواها كنص؛ بينما يطبع %v الأرقام ([104 105]).
أنواع أخرى
| الفعل | النوع | ما يطبعه |
|---|---|---|
%t | bool | true أو false |
%p | مؤشر، شريحة، خريطة، قناة، دالة | العنوان، مثل 0xc000012345 |
%w | error (فقط في Errorf) | رسالة الخطأ، ويغلّفه |
العرض والدقة والحشو
بين % والفعل يمكنك وضع أعلام وعرض ودقة:
| الصيغة | الأثر |
|---|---|
%5d | عرض 5، محاذاة إلى النهاية (حشو بمسافات قبل القيمة) |
%-5d | عرض 5، محاذاة إلى البداية |
%05d | حشو بالأصفار |
%.2f | رقمان بعد الفاصلة العشرية |
%8.2f | عرض 8 ومنزلتان عشريتان |
%.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 واحد عدة أفعال %w. صفحة معالجة الأخطاء تشرح التغليف بالتفصيل.
تنسيق مخصّص بـ String()
أي نوع له تابع String() string يتحكّم في طريقة عرض %v و%s وPrintln له:
يتجاوز %d التابع String() ويطبع الرقم الأساسي. في أنواع الأخطاء التابع المقابل هو Error() string، وله الأسبقية على String().
عندما يكون الفعل خاطئًا
لا تسبّب 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)
هذه المخرجات تصل غالبًا إلى الإنتاج لأنها لا توقف شيئًا. يكتشف 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 للبنى والشرائح والخرائط)، وهذا مقبول للتسجيل والمخرجات لكنه ملموس في الحلقات الضيّقة. لتحويل رقم واحد، 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.
كيف أطبع بنية مع أسماء حقولها في Go؟
استخدم %+v: تطبع fmt.Printf("%+v\n", user) القيمة {Name:Ana Age:31}. أما %v فيطبع القيم فقط، {Ana 31}، و%#v يطبع صيغة Go مع النوع، main.User{Name:"Ana", Age:31}.
كيف أنسّق عددًا عشريًا بمنزلتين عشريتين في 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) صحيحة. يعمل %w فقط في fmt.Errorf.
لماذا تظهر في مخرجاتي %!d(string=...)؟
الفعل لا يطابق نوع الوسيط، مثلًا %d مع نص. تطبع fmt المشكلة في مكانها بدل panic: %!d(string=oops). الوسائط الناقصة تطبع %!d(MISSING) والزائدة %!(EXTRA int=2). يكتشف go vet الحالات الثلاث قبل تشغيل البرنامج.