Menu

Chuỗi trong Golang: Split, Join, Contains, Replace, Trim

Làm việc với chuỗi trong Go: package strings (Split, Join, Contains, Replace, Fields, TrimSpace, ToUpper), ghép chuỗi hiệu quả với strings.Builder, chuỗi raw nhiều dòng, tính bất biến, và vì sao len đếm byte.

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.

Chuỗi (string) trong Go là một dãy byte bất biến, thường là văn bản UTF-8. Các toán tử làm việc trực tiếp trên chuỗi (+, ==, <, truy cập theo chỉ số, cắt chuỗi), còn mọi thứ khác nằm trong package chuẩn strings.

Tách chuỗi

HàmTách tạiGhi chú
Split(s, sep)mọi sepgiữ phần rỗng; Split("", ",") trả về [""], một chuỗi rỗng
SplitN(s, sep, n)tối đa n phầnphần cuối chứa toàn bộ phần còn lại
SplitAfter(s, sep)mọi sepgiữ dấu phân cách ở cuối mỗi phần
Fields(s)các đoạn khoảng trắngkhông bao giờ trả về phần rỗng
FieldsFunc(s, f)các ký tự mà f trả về truedấu phân cách tùy chỉnh
Cut(s, sep)chỉ sep đầu tiêntrả về phần trước, phần sau, có tìm thấy hay không (Go 1.18)

Trường hợp Split("", ",") hay làm người ta vấp: đầu vào rỗng cho ra slice độ dài 1, không phải 0. Khi tách dữ liệu người dùng nhập, dùng Fields hoặc kiểm tra s == "" trước để tránh một phần tử rỗng ma.

strings.Cut là cách gọn nhất để tách key=value, user@host và các cặp tương tự. Go 1.24 còn thêm phiên bản iterator, strings.SplitSeq và strings.FieldsSeq, trả về từng phần một mà không dựng slice.

Tìm kiếm

HàmTrả về
Contains(s, sub)true nếu sub xuất hiện trong s
ContainsAny(s, chars)true nếu có ký tự bất kỳ của chars xuất hiện
HasPrefix(s, p) / HasSuffix(s, p)true nếu s bắt đầu hoặc kết thúc bằng p
Index(s, sub)vị trí byte của kết quả khớp đầu tiên, hoặc -1
LastIndex(s, sub)vị trí byte của kết quả khớp cuối cùng, hoặc -1
Count(s, sub)số kết quả khớp không chồng lên nhau
EqualFold(a, b)so sánh bằng nhau không phân biệt hoa thường

Mọi phép tìm kiếm đều phân biệt hoa thường, trừ EqualFold. Để tìm không phân biệt hoa thường, hãy đổi cả hai bên sang chữ thường hoặc dùng EqualFold khi so sánh cả chuỗi.

Biến đổi chuỗi

Một cái bẫy: Trim, TrimLeft và TrimRight nhận một tập ký tự, không phải tiền tố. strings.TrimLeft("abcab", "ab") bỏ mọi a và b ở đầu và trả về "cab". Để bỏ đúng một tiền tố hay hậu tố, hãy dùng TrimPrefix và TrimSuffix.

Nối chuỗi và strings.Builder

+ nối các chuỗi, còn fmt.Sprintf định dạng các giá trị thành một chuỗi. Cả hai đều cấp phát chuỗi mới mỗi lần, vì chuỗi là bất biến. Với vài thao tác thì không sao, nhưng trong vòng lặp sẽ chậm, vì mỗi lần += sao chép mọi thứ đã ghép được. Hãy dùng strings.Builder:

Một strings.Builder ở giá trị zero là dùng được ngay. Nó cài đặt io.Writer, nên fmt.Fprintf ghi thẳng vào nó. Gọi b.Grow(n) trước nếu bạn biết đại khái kết quả lớn cỡ nào. Đừng sao chép một Builder sau khi đã ghi vào nó; hãy truyền con trỏ.

Chuỗi nhiều dòng và chuỗi raw

Go có hai loại literal chuỗi:

Chuỗi trong dấu nháy kép xử lý ký tự thoát (\n, \t, \", \\, \u00e9) và phải nằm trên một dòng. Chuỗi raw trong dấu backquote giữ nguyên mọi thứ, kể cả xuống dòng, nên đây là cách viết chuỗi nhiều dòng, biểu thức chính quy, JSON và đường dẫn Windows. Chuỗi raw không thể chứa dấu backquote, và ký tự carriage return bên trong bị loại bỏ. Lưu ý câu truy vấn ở trên bắt đầu bằng một dòng trống, vì literal bắt đầu ngay sau dấu backquote mở.

Độ dài, chỉ số và chuỗi con

len(s) là số byte. s[i] là một byte, và s[i:j] là chuỗi con theo vị trí byte:

ï và é mỗi ký tự chiếm hai byte, nên chuỗi dài 12 byte và có 10 ký tự. Cắt theo byte là an toàn miễn là vị trí lấy từ strings.Index hoặc hàm tương tự, vốn trả về vị trí byte nằm đúng ranh giới ký tự. Để lấy "n ký tự đầu tiên", hãy chuyển sang []rune trước. Trang rune và byte giải thích UTF-8 chi tiết.

Chuỗi là bất biến

Bạn không thể sửa một byte tại chỗ: s[0] = 'H' là lỗi biên dịch (cannot assign to s[0] (neither addressable nor a map index expression)). Mọi hàm trong strings đều trả về chuỗi mới. Để sửa, hãy chuyển sang []byte hoặc []rune, thay đổi slice, rồi chuyển ngược lại:

b := []byte("hello")
b[0] = 'H'
s := string(b) // "Hello"

Tính bất biến cũng là lý do chuỗi con rất rẻ: s[2:5] dùng chung bộ nhớ với s thay vì sao chép. Mặt trái là một chuỗi con nhỏ của một chuỗi khổng lồ sẽ giữ cả chuỗi khổng lồ đó trong bộ nhớ. Hãy dùng strings.Clone (Go 1.20) khi bạn giữ lại một đoạn ngắn của một đầu vào lớn.

So sánh chuỗi

== và != so sánh nội dung, còn <, > so sánh từng byte, tức là theo thứ tự bảng chữ cái với ASCII. strings.Compare có tồn tại nhưng hiếm khi cần; slices.Sort trên []string dùng <. Để sắp xếp văn bản không phải tiếng Anh theo quy tắc ngôn ngữ, hãy dùng golang.org/x/text/collate.

Chuyển đổi từ kiểu khác

Số được chuyển thành chuỗi bằng strconv.Itoa, strconv.FormatFloat hoặc fmt.Sprintf, còn chuỗi thành số bằng strconv.Atoi và strconv.ParseFloat. Đừng viết string(42): nó tạo ra ký tự có code point 42, tức "*". Trang về chuyển đổi kiểu có bảng đầy đủ.

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

Làm sao tách chuỗi trong Go?

Dùng strings.Split(s, sep): strings.Split("a,b,c", ",") trả về ["a" "b" "c"]. Để tách theo mọi khoảng trắng liên tiếp và bỏ phần rỗng, dùng strings.Fields(s). strings.SplitN giới hạn số phần, còn strings.Cut tách một lần quanh dấu phân cách đầu tiên.

Làm sao viết chuỗi nhiều dòng trong Go?

Dùng literal chuỗi raw trong dấu backquote:

query := `SELECT id, name
FROM users
WHERE active = true`

Mọi thứ giữa hai dấu backquote được giữ nguyên văn, kể cả xuống dòng và dấu gạch chéo ngược. Chuỗi raw không thể chứa dấu backquote.

Làm sao kiểm tra chuỗi có chứa chuỗi con trong Go?

strings.Contains(s, substr) trả về một bool. Các hàm liên quan: strings.HasPrefix, strings.HasSuffix, strings.Index (vị trí hoặc -1), strings.ContainsAny (bất kỳ ký tự nào trong một tập), và strings.EqualFold để so sánh bằng nhau không phân biệt hoa thường.

Cách nối chuỗi nhanh nhất trong Go là gì?

Với vài chuỗi, + là đủ. Khi ghép chuỗi trong vòng lặp, hãy dùng strings.Builder: gọi WriteString nhiều lần và String() một lần ở cuối. += lặp lại trong vòng lặp sao chép toàn bộ chuỗi mỗi lần, nên càng chậm khi chuỗi càng dài. Để nối một slice với dấu phân cách, dùng strings.Join.

Vì sao len trả về độ dài chuỗi sai?

len(s) trả về số byte, không phải số ký tự. Chuỗi Go là UTF-8, trong đó ký tự ngoài ASCII chiếm từ 2 đến 4 byte, nên len("héllo") là 6. Dùng utf8.RuneCountInString(s) để lấy số code point Unicode.

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