Menu

Golang int sang string, string sang int và ép kiểu

Cách chuyển đổi kiểu trong Go: int sang string và string sang int bằng strconv, số thực sang số nguyên, chuyển đổi số bằng T(v), byte và rune, cái bẫy string(65), và cách xử lý lỗi khi chuyển đổi.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Go không bao giờ tự ngầm chuyển đổi kiểu. Bạn chuyển giữa các kiểu tương thích bằng T(v), và giữa số với văn bản bằng package strconv. Hai kiểu chuyển đổi được tìm nhiều nhất:

strconv.Itoa ("integer to ASCII") định dạng một int thành văn bản thập phân. strconv.Atoi ("ASCII to integer") phân tích văn bản thành một int và trả về lỗi khi văn bản không phải số hợp lệ. Luôn kiểm tra lỗi đó.

Chuyển đổi bằng T(v)

Cú pháp chuyển đổi của Go trông như một lời gọi hàm mang tên kiểu đích. Nó dùng được trong ba trường hợp:

  1. giữa các kiểu số (int, int64, float64, uint8, ...),
  2. giữa string và []byte hoặc []rune,
  3. giữa các kiểu có chung kiểu nền (Celsius và float64).

Đọc kỹ kết quả:

  • int(3.9) là 3 và int(-3.9) là -3. Chuyển đổi cắt bỏ phần thập phân về phía 0; nó không làm tròn. Hãy dùng math.Round, math.Floor hoặc math.Ceil trước để chọn cách xử lý. math.Round làm tròn số .5 ra xa số 0, nên -3.5 thành -4.
  • uint8(300) là 44, vì chỉ 8 bit thấp được giữ lại (300 trừ 256), và int8(200) là -56 cũng vì lý do đó. Không có lỗi, không có panic.
  • Celsius và float64 có chung kiểu nền, nên chuyển qua lại thoải mái, nhưng phải viết tường minh.

Các giá trị được đặt trong biến là có chủ ý. Với hằng số, cùng những phép chuyển đổi đó sẽ không biên dịch được.

Hằng số được kiểm tra, biến thì không

Chuyển một hằng số không vừa với kiểu đích là lỗi biên dịch. Chuyển một biến có cùng giá trị thì lặng lẽ bị tràn vòng:

fmt.Println(uint8(300)) // compile error
x := 300
fmt.Println(uint8(x)) // 44
./main.go:6:20: constant 300 overflows uint8

Chuyển một hằng số không nguyên sang kiểu số nguyên cũng bị từ chối, trong khi cùng giá trị đó nằm trong biến thì bị cắt phần thập phân:

./main.go:7:18: cannot convert -3.9 (untyped float constant) to type int

Nếu giá trị số thực nằm ngoài phạm vi của kiểu số nguyên đích (ví dụ int64(1e20)), kết quả phụ thuộc vào cài đặt: chẳng hạn amd64 và arm64 cho ra kết quả khác nhau. Hãy kiểm tra phạm vi trước khi chuyển đổi những giá trị không đáng tin.

Chuyển số sang chuỗi

TừHàmVí dụKết quả
intstrconv.Itoa(n)strconv.Itoa(-7)"-7"
int64strconv.FormatInt(n, base)strconv.FormatInt(255, 16)"ff"
uint64strconv.FormatUint(n, base)strconv.FormatUint(5, 2)"101"
float64strconv.FormatFloat(f, fmt, prec, 64)strconv.FormatFloat(3.14159, 'f', 2, 64)"3.14"
boolstrconv.FormatBool(b)strconv.FormatBool(true)"true"
mọi kiểufmt.Sprint(v)fmt.Sprint(3.5)"3.5"
mọi kiểu, có định dạngfmt.Sprintf(format, v)fmt.Sprintf("%05d", 42)"00042"

FormatFloat nhận một byte định dạng ('f' cho dạng thập phân cố định, 'e' cho dạng số mũ, 'g' cho dạng nào ngắn hơn), một độ chính xác (-1 nghĩa là "ít chữ số nhất mà vẫn khôi phục chính xác được"), và kích thước bit của số thực gốc (64 hoặc 32).

strconv nhanh hơn fmt.Sprint, vì fmt.Sprint đóng gói giá trị vào một interface và đi qua đường định dạng tổng quát của fmt. Trong một vòng lặp chạy nhiều, khác biệt này thấy rõ; với những lần chuyển đổi thỉnh thoảng, chọn cách nào dễ đọc hơn là được.

Chuyển chuỗi sang số

SangHàmTrả về
intstrconv.Atoi(s)int, error
mọi kiểu số nguyên có dấustrconv.ParseInt(s, base, bitSize)int64, error
mọi kiểu số nguyên không dấustrconv.ParseUint(s, base, bitSize)uint64, error
số thựcstrconv.ParseFloat(s, bitSize)float64, error
boolstrconv.ParseBool(s)bool, error

ParseInt luôn trả về int64. Đối số bitSize (0, 8, 16, 32, 64) quy định phạm vi nó chấp nhận, nên kết quả chắc chắn vừa khi bạn chuyển xuống kiểu nhỏ hơn. base bằng 0 nghĩa là "suy ra từ tiền tố", nên 0x1f, 0o17, 0b101 và 1_000 đều phân tích được.

Những điều đầu ra cho thấy:

  • " 42" thất bại. Atoi chỉ chấp nhận dấu tùy chọn và chữ số, không gì khác. Đầu vào từ bufio thường kết thúc bằng \n, nên hãy cắt nó đi.
  • "4.2" không hợp lệ với Atoi. Dùng ParseFloat cho số thập phân.
  • Khi gặp lỗi vượt phạm vi, ParseInt trả về giá trị lớn nhất còn vừa (127 với int8) kèm theo lỗi. Đừng dùng giá trị đó khi lỗi khác nil.
  • Các lỗi là giá trị *strconv.NumError bọc strconv.ErrSyntax hoặc strconv.ErrRange, nên errors.Is phân biệt được chúng.

ParseBool chấp nhận 1, t, T, TRUE, true, True và các dạng false tương ứng. "yes" và "on" là lỗi.

Cái bẫy string(65)

Chuyển một số nguyên sang string không tạo ra chữ số. Nó tạo ra ký tự có code point Unicode đó:

n := 65
s := string(n) // "A", not "65"

Trình biên dịch chấp nhận, nhưng go vet sẽ báo:

./main.go:10:7: conversion from int to string yields a string of one rune, not a string of digits

Nếu bạn thật sự muốn lấy ký tự, hãy nói rõ bằng rune: string(rune(n)). Nếu bạn muốn con số dưới dạng văn bản, dùng strconv.Itoa(n). Chuyển một rune hoặc byte sang string thì hoàn toàn bình thường và rất hay gặp:

Chuỗi, byte và rune

string, []byte và []rune chuyển qua lại với nhau bằng T(v). Mỗi lần chuyển đổi đều sao chép dữ liệu, vì chuỗi là bất biến còn slice thì không:

Chuyển sang []rune khi bạn cần truy cập hoặc sửa theo từng ký tự; chuyển sang []byte cho I/O, băm và chỉnh sửa ở mức byte. Trang rune và byte trình bày chi tiết về UTF-8.

Chuyển đổi kiểu và type assertion

Chuyển đổi kiểu biến một giá trị từ kiểu cụ thể này sang kiểu cụ thể khác. Lấy giá trị cụ thể ra khỏi một interface (any, error, io.Reader) là một thao tác khác, gọi là 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

Assert trước, rồi mới chuyển đổi. Dạng comma-ok n, ok := v.(int) tránh được panic khi kiểu không đúng.

Tra cứu nhanh

Việc cần làmCode
int sang stringstrconv.Itoa(n)
int64 sang stringstrconv.FormatInt(n, 10)
string sang intn, err := strconv.Atoi(s)
string sang int64n, err := strconv.ParseInt(s, 10, 64)
string sang float64f, err := strconv.ParseFloat(s, 64)
float64 sang stringstrconv.FormatFloat(f, 'f', -1, 64)
float64 sang intint(f) (cắt phần thập phân) hoặc int(math.Round(f))
int sang float64float64(n)
string sang []byte[]byte(s)
[]byte sang stringstring(b)
rune sang stringstring(r)
bool sang stringstrconv.FormatBool(b)
mọi giá trị sang stringfmt.Sprint(v)

Câu hỏi thường gặp

Chuyển int sang string trong Go như thế nào?

Dùng strconv.Itoa(n): strconv.Itoa(42) trả về "42". Với int64, dùng strconv.FormatInt(n, 10). fmt.Sprint(n) cũng dùng được với mọi kiểu nhưng chậm hơn. Đừng viết string(n): cách đó coi n là một code point Unicode, nên string(65) ra "A".

Chuyển string sang int trong Go như thế nào?

Dùng strconv.Atoi, hàm này trả về con số và một lỗi:

n, err := strconv.Atoi("42")
if err != nil {
	// not a valid integer
}

Nếu cần kích thước hoặc cơ số cụ thể, dùng strconv.ParseInt(s, 10, 64), hàm này trả về int64. Atoi không tự cắt khoảng trắng, nên " 42" là lỗi; hãy gọi strings.TrimSpace trước.

Ép kiểu trong Go như thế nào?

Go gọi đó là chuyển đổi kiểu (conversion), và cú pháp là T(v): float64(n), int(f), []byte(s). Nó dùng được giữa các kiểu số, giữa chuỗi với slice byte hoặc rune, và giữa các kiểu có cùng kiểu nền. Nó không phân tích văn bản: biến "42" thành 42 cần đến strconv. Lấy kiểu cụ thể ra khỏi một interface là type assertion, v.(T), chứ không phải chuyển đổi kiểu.

Chuyển float sang int trong Go như thế nào?

int(f) cắt bỏ phần thập phân về phía 0: int(3.9) là 3 và int(-3.9) là -3. Để làm tròn, dùng int(math.Round(f)). Nếu số thực nằm ngoài phạm vi của kiểu số nguyên, kết quả phụ thuộc vào nền tảng, nên hãy kiểm tra phạm vi trước khi giá trị đến từ đầu vào.

Vì sao string(65) trong Go trả về "A"?

Chuyển một số nguyên sang string sẽ hiểu số đó là một code point Unicode, và 65 là code point của A. go vet sẽ cảnh báo: conversion from int to string yields a string of one rune, not a string of digits. Dùng strconv.Itoa(65) để có "65".

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu