لا تحوّل Go الأنواع ضمنيًا أبدًا. تحوّل بين الأنواع المتوافقة بـ T(v)، وبين الأرقام والنصوص بالحزمة strconv. أكثر تحويلين يُبحث عنهما:
تنسّق strconv.Itoa ("integer to ASCII") قيمة int كنص عشري. وتحلّل strconv.Atoi ("ASCII to integer") النص إلى int وتعيد خطأ عندما لا يكون النص رقمًا صالحًا. افحص ذلك الخطأ دائمًا.
التحويل T(v)
تبدو صياغة التحويل في Go كاستدعاء دالة باسم النوع الهدف. تعمل في ثلاث حالات:
- بين الأنواع الرقمية (
intوint64وfloat64وuint8، ...)، - بين
stringو[]byteأو[]rune، - بين الأنواع التي تتشارك نوعًا أساسيًا (
Celsiusوfloat64).
اقرأ النتائج بعناية:
int(3.9)تساوي3وint(-3.9)تساوي-3. التحويل يقتطع نحو الصفر؛ ولا يقرّب. استخدمmath.Roundأوmath.Floorأوmath.Ceilأولًا لتختار السلوك. تقرّبmath.Roundالنصف بعيدًا عن الصفر، فتصبح-3.5مساوية لـ-4.uint8(300)تساوي44، لأن البتات الثمانية الدنيا وحدها تبقى (300 ناقص 256)، وint8(200)تساوي-56للسبب نفسه. لا خطأ ولا panic.- يتشارك
Celsiusوfloat64نوعًا أساسيًا، فيتحوّلان بحرية، لكن صراحة فقط.
القيم في متغيّرات عن قصد. مع الثوابت لا تُترجم التحويلات نفسها.
الثوابت تُفحص، والمتغيّرات لا
تحويل ثابت لا يتّسع خطأ ترجمة. أما تحويل متغيّر بالقيمة نفسها فيلتفّ بصمت:
fmt.Println(uint8(300)) // compile error
x := 300
fmt.Println(uint8(x)) // 44
./main.go:6:20: constant 300 overflows uint8
وتحويل ثابت غير صحيح إلى نوع صحيح مرفوض أيضًا، بينما تُقتطع القيمة نفسها في متغيّر:
./main.go:7:18: cannot convert -3.9 (untyped float constant) to type int
إذا كانت القيمة العشرية خارج نطاق النوع الصحيح الهدف (مثل int64(1e20))، فالنتيجة تعتمد على التطبيق: amd64 وarm64 مثلًا يعطيان إجابتين مختلفتين. افحص النطاق قبل تحويل القيم غير الموثوقة.
تحويل الأرقام إلى نصوص
| من | الدالة | مثال | النتيجة |
|---|---|---|---|
int | strconv.Itoa(n) | strconv.Itoa(-7) | "-7" |
int64 | strconv.FormatInt(n, base) | strconv.FormatInt(255, 16) | "ff" |
uint64 | strconv.FormatUint(n, base) | strconv.FormatUint(5, 2) | "101" |
float64 | strconv.FormatFloat(f, fmt, prec, 64) | strconv.FormatFloat(3.14159, 'f', 2, 64) | "3.14" |
bool | strconv.FormatBool(b) | strconv.FormatBool(true) | "true" |
| أي شيء | fmt.Sprint(v) | fmt.Sprint(3.5) | "3.5" |
| أي شيء، منسّقًا | fmt.Sprintf(format, v) | fmt.Sprintf("%05d", 42) | "00042" |
تأخذ FormatFloat بايت صيغة ('f' للصيغة الثابتة، و'e' للأسّ، و'g' لأيهما أقصر)، ودقة (-1 تعني "أقل عدد من الأرقام يعيد القيمة نفسها بدقة")، وحجم البتات للعدد العشري الأصلي (64 أو 32).
strconv أسرع من fmt.Sprint، التي تضع القيمة في واجهة وتمرّ بمسار التنسيق العام في fmt. في حلقة ساخنة يظهر هذا الفرق؛ وللتحويلات العرضية اختر ما يُقرأ أفضل.
تحويل النصوص إلى أرقام
| إلى | الدالة | تعيد |
|---|---|---|
int | strconv.Atoi(s) | int, error |
| أي عدد صحيح بإشارة | strconv.ParseInt(s, base, bitSize) | int64, error |
| أي عدد صحيح دون إشارة | strconv.ParseUint(s, base, bitSize) | uint64, error |
| عدد عشري | strconv.ParseFloat(s, bitSize) | float64, error |
| قيمة منطقية | strconv.ParseBool(s) | bool, error |
تعيد ParseInt دائمًا int64. يضبط الوسيط bitSize (0 أو 8 أو 16 أو 32 أو 64) النطاق الذي تقبله، فتكون النتيجة مضمونة الاتّساع عند تحويلها إلى نوع أصغر. والأساس base بقيمة 0 يعني "استنتج من البادئة"، فتُحلَّل 0x1f و0o17 و0b101 و1_000 كلها.
أمور تظهرها المخرجات:
- يفشل
" 42". تقبلAtoiإشارة اختيارية وأرقامًا، ولا شيء آخر. المدخلات منbufioتنتهي عادة بـ\n، فأزلها. "4.2"غير صالح لـAtoi. استخدمParseFloatللأعداد العشرية.- عند خطأ خارج النطاق تعيد
ParseIntأكبر قيمة تتّسع (127لـint8) مع الخطأ. لا تستخدم القيمة عندما لا يكون الخطأ nil. - الأخطاء قيم
*strconv.NumErrorتغلّفstrconv.ErrSyntaxأوstrconv.ErrRange، فتستطيعerrors.Isالتمييز بينها.
تقبل ParseBool القيم 1 وt وT وTRUE وtrue وTrue وأشكال false المقابلة. أما "yes" و"on" فأخطاء.
فخ string(65)
تحويل عدد صحيح إلى string لا ينتج أرقامًا. بل ينتج الحرف صاحب نقطة رمز Unicode تلك:
n := 65
s := string(n) // "A", not "65"
يقبله المترجم، لكن go vet يبلغ عنه:
./main.go:10:7: conversion from int to string yields a string of one rune, not a string of digits
إذا أردت الحرف فعلًا، فقل ذلك بـ rune: string(rune(n)). وإذا أردت الرقم كنص، فاستخدم strconv.Itoa(n). أما تحويل rune أو byte إلى string فلا بأس به وشائع:
النصوص والبايتات والـ runes
تتحوّل string و[]byte و[]rune فيما بينها بـ T(v). كل تحويل ينسخ البيانات، لأن النصوص غير قابلة للتعديل والشرائح قابلة:
حوّل إلى []rune عندما تحتاج الفهرسة أو التعديل بالأحرف؛ وحوّل إلى []byte للإدخال والإخراج والتجزئة والتعديلات على مستوى البايت. صفحة الـ runes والبايتات تشرح UTF-8 بالتفصيل.
التحويل مقابل تأكيد النوع
يغيّر التحويل قيمة من نوع محدّد إلى آخر. أما استخراج القيمة المحدّدة من واجهة (any أو error أو io.Reader) فعملية مختلفة، هي تأكيد النوع:
var v any = 42
n := v.(int) // type assertion: v holds an int, give it to me
f := float64(n) // conversion: int to float64
// f := float64(v) // compile error: cannot convert v (variable of interface type any)
// // to type float64: need type assertion
أكّد أولًا، ثم حوّل. وصيغة comma-ok n, ok := v.(int) تتجنّب panic عندما يكون النوع خاطئًا.
مرجع سريع
| المهمة | الشيفرة |
|---|---|
| من int إلى string | strconv.Itoa(n) |
| من int64 إلى string | strconv.FormatInt(n, 10) |
| من string إلى int | n, err := strconv.Atoi(s) |
| من string إلى int64 | n, err := strconv.ParseInt(s, 10, 64) |
| من string إلى float64 | f, err := strconv.ParseFloat(s, 64) |
| من float64 إلى string | strconv.FormatFloat(f, 'f', -1, 64) |
| من float64 إلى int | int(f) (يقتطع) أو int(math.Round(f)) |
| من int إلى float64 | float64(n) |
| من string إلى []byte | []byte(s) |
| من []byte إلى string | string(b) |
| من rune إلى string | string(r) |
| من bool إلى string | strconv.FormatBool(b) |
| أي قيمة إلى string | fmt.Sprint(v) |
الأسئلة الشائعة
كيف أحوّل int إلى string في Go؟
استخدم strconv.Itoa(n): تعيد strconv.Itoa(42) القيمة "42". ولـ int64 استخدم strconv.FormatInt(n, 10). تعمل fmt.Sprint(n) أيضًا مع أي نوع لكنها أبطأ. لا تكتب string(n): فهذا يعامل n كنقطة رمز Unicode، فتكون string(65) مساوية لـ "A".
كيف أحوّل string إلى int في Go؟
استخدم strconv.Atoi، التي تعيد الرقم وخطأ:
n, err := strconv.Atoi("42")
if err != nil {
// not a valid integer
}
لحجم أو أساس محدّد استخدم strconv.ParseInt(s, 10, 64)، التي تعيد int64. لا تزيل Atoi المسافات، فيكون " 42" خطأ؛ استدعِ strings.TrimSpace أولًا.
كيف أجري cast في Go؟
تسمّيه Go تحويلًا، وصياغته T(v): float64(n) وint(f) و[]byte(s). يعمل بين الأنواع الرقمية، وبين النصوص وشرائح البايتات أو الـ runes، وبين الأنواع التي لها النوع الأساسي نفسه. ولا يحلّل النصوص: تحويل "42" إلى 42 يحتاج strconv. أما استخراج نوع محدّد من واجهة فهو تأكيد نوع، v.(T)، لا تحويل.
كيف أحوّل float إلى int في Go؟
يقتطع int(f) نحو الصفر: int(3.9) تساوي 3 وint(-3.9) تساوي -3. للتقريب استخدم int(math.Round(f)). وإذا كان العدد العشري خارج نطاق النوع الصحيح فالنتيجة تعتمد على التطبيق، فافحص النطاق أولًا عندما تأتي القيمة من مدخلات.
لماذا تعيد string(65) القيمة "A" في Go؟
تحويل عدد صحيح إلى string يفسّره كنقطة رمز Unicode، و65 هي نقطة رمز A. يبلغ go vet عن ذلك: conversion from int to string yields a string of one rune, not a string of digits. استخدم strconv.Itoa(65) لتحصل على "65".