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, כי רק 8 הביטים הנמוכים שורדים (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 פירושו "הכי מעט ספרות שמאפשרות שחזור מדויק"), וגודל הביטים של ה-float המקורי (64 או 32).
strconv מהירה יותר מ-fmt.Sprint, שעוטפת את הערך ב-interface ועוברת במסלול העיצוב הכללי של fmt. בלולאה חמה ההבדל הזה מורגש; להמרות מזדמנות, בחרו במה שנקרא טוב יותר.
המרת מחרוזות למספרים
| אל | פונקציה | מחזירה |
|---|---|---|
int | strconv.Atoi(s) | int, error |
| כל int עם סימן | strconv.ParseInt(s, base, bitSize) | int64, error |
| כל int בלי סימן | strconv.ParseUint(s, base, bitSize) | uint64, error |
| float | strconv.ParseFloat(s, bitSize) | float64, error |
| bool | 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 לא מייצרת ספרות. היא מייצרת את התו עם ה-code point הזה של 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 תקינה ונפוצה:
מחרוזות, bytes ו-runes
string, []byte ו-[]rune מומרים זה לזה עם T(v). כל המרה מעתיקה את הנתונים, כי מחרוזות הן immutable ו-slices לא:
המירו ל-[]rune כשצריך לגשת לפי אינדקס או לשנות לפי תו; המירו ל-[]byte לקלט ופלט, ל-hashing ולעריכות ברמת הבית. הדף על runes ו-bytes מכסה UTF-8 בפירוט.
המרה מול type assertion
המרה משנה ערך מטיפוס קונקרטי אחד לאחר. הוצאת הערך הקונקרטי מתוך interface (any, error, io.Reader) היא פעולה אחרת, ה-type assertion:
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
קודם assertion, אחר כך המרה. צורת ה-comma-ok n, ok := v.(int) מונעת panic כשהטיפוס שגוי.
תקציר מהיר
| משימה | קוד |
|---|---|
| int למחרוזת | strconv.Itoa(n) |
| int64 למחרוזת | strconv.FormatInt(n, 10) |
| מחרוזת ל-int | n, err := strconv.Atoi(s) |
| מחרוזת ל-int64 | n, err := strconv.ParseInt(s, 10, 64) |
| מחרוזת ל-float64 | f, err := strconv.ParseFloat(s, 64) |
| float64 למחרוזת | strconv.FormatFloat(f, 'f', -1, 64) |
| float64 ל-int | int(f) (מקצץ) או int(math.Round(f)) |
| int ל-float64 | float64(n) |
| מחרוזת ל-[]byte | []byte(s) |
| []byte למחרוזת | string(b) |
| rune למחרוזת | string(r) |
| bool למחרוזת | strconv.FormatBool(b) |
| כל ערך למחרוזת | fmt.Sprint(v) |
שאלות נפוצות
איך ממירים int למחרוזת ב-Go?
השתמשו ב-strconv.Itoa(n): strconv.Itoa(42) מחזירה "42". ל-int64 השתמשו ב-strconv.FormatInt(n, 10). גם fmt.Sprint(n) עובדת לכל טיפוס אבל היא איטית יותר. אל תכתבו string(n): זה מתייחס ל-n כאל code point של Unicode, ולכן string(65) הוא "A".
איך ממירים מחרוזת ל-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). זה עובד בין טיפוסים מספריים, בין מחרוזות ל-slices של bytes או runes, ובין טיפוסים עם אותו טיפוס בסיס. זה לא מפענח טקסט: הפיכת "42" ל-42 דורשת strconv. הוצאת טיפוס קונקרטי מתוך interface היא type assertion, v.(T), לא המרה.
איך ממירים float ל-int ב-Go?
int(f) מקצץ לכיוון אפס: int(3.9) הוא 3 ו-int(-3.9) הוא -3. כדי לעגל, השתמשו ב-int(math.Round(f)). אם ה-float מחוץ לטווח של טיפוס המספר השלם, התוצאה תלויה במימוש, אז בדקו את הטווח קודם כשהערך מגיע מקלט.
למה string(65) מחזיר "A" ב-Go?
המרה של מספר שלם ל-string מפרשת אותו כ-code point של Unicode, ו-65 הוא ה-code point של A. go vet מסמן את זה: conversion from int to string yields a string of one rune, not a string of digits. השתמשו ב-strconv.Itoa(65) כדי לקבל "65".