[Golang cơ bản] Các loại dữ liệu cơ bản trong golang
Post on: 2023-03-29 00:12:59 | in: Golang
Các kiểu dữ liệu cơ bản trong Golang bao gồm số nguyên, số thực, chuỗi, boolean, mảng, slice, map, struct và con trỏ.
- [Golang cơ bản] Function trong Golang - Tính năng và cách sử dụng
- [Golang cơ bản] Constant trong Golang
- [Golang cơ bản] Vòng lặp for trong golang
Tổng quan
Golang là một ngôn ngữ lập trình kiểu tĩnh, có nghĩa là mỗi biến có một kiểu. Golang có một vài kiểu dữ liệu tích hợp sẵn. Các kiểu dữ liệu trong Golang có thể được phân loại thành hai loại.
- Kiểu Cơ bản
- Kiểu tổng hợp
- Kiểu Cơ bản
- Integers
- Signed
- int
- int8
- int16
- int32
- int64
- Unsigned
- uint
- uint8
- uint16
- uint32
- uint64
- uintptr
- Signed
- Floats
- float32
- float64
- Complex Numbers
- complex64
- complex128
- Byte
- Rune
- String
- Boolean
- Integers
- Kiểu tổng hợp
- Tập hợp/Tổng hợp hoặc Kiểu Không Tham chiếu
- Arrays
- Structs
- Kiểu Tham chiếu
- Slices
- Maps
- Channels
- Pointers
- Function/Methods
- Interface
- Special case of empty Interface
- Tập hợp/Tổng hợp hoặc Kiểu Không Tham chiếu
Kiểu cơ bản
Trong bài viết này chúng ta chỉ sẽ thảo luận về các kiểu cơ bản.
Integers
Các số nguyên có thể được ký hiệu hoặc không ký hiệu.
Signed Integers
Số nguyên ký hiệu bao gồm 5 loại như dưới đây:
Type | Size |
int | Phụ thuộc vào nền tảng |
int8 | 8 bits/1 byte |
int16 | 16 bits/2 byte |
int32 | 32 bits/4 byte |
int64 | 64 bits/8 byte |
int
Kích thước: phụ thuộc vào nền tảng.
- Trên máy tính 32 bit, kích thước của int sẽ là 32 bit hoặc 4 byte.
- Trên máy tính 64 bit, kích thước của int sẽ là 64 bit hoặc 8 byte.
Phạm vi: cũng phụ thuộc vào nền tảng.
- Trên máy tính 32 bit, phạm vi của int sẽ là từ -2^31 đến 2^31 -1.
- Trên máy tính 64 bit, phạm vi của int sẽ là từ -2^63 đến 2^63 -1.
Khi nào sử dụng:
- Nên sử dụng int khi sử dụng số nguyên có dấu ngoại trừ các trường hợp được đề cập dưới đây.
- Khi máy là 32 bit và phạm vi cần thiết lớn hơn -2^31 đến 2^31 -1, thì sử dụng int64 thay vì int. Lưu ý rằng trong trường hợp này cho int64, cần sử dụng 2 địa chỉ bộ nhớ 32 bit để tạo thành một số 64 bit cùng nhau.
- Khi phạm vi nhỏ hơn thì sử dụng loại số nguyên phù hợp.
Thuộc tính:
- Khai báo một biến int.
var a int
- int là kiểu mặc định cho số nguyên. Khi bạn không chỉ định kiểu, kiểu mặc định sẽ là int.
b := 2 //The default is also intfmt.Println(reflect.TypeOf(b)) => int
- Package bits trong Golang có thể giúp bạn biết kích thước của một kiểu số nguyên (int) trên hệ thống của bạn.
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64sizeOfIntInBits := bits.UintSizefmt.Println(sizeOfIntInBits) => 32 0r 34
- Hàm unsafe.Sizeof() cũng có thể được sử dụng để xem kích thước của kiểu int tính bằng byte.
Mã hoạt động đầy đủ.
Dưới đây là mã nguồn đầy đủ và hoạt động của các thuộc tính đã nêu ở trên:
package main
import (
"fmt"
"math/bits"
"reflect"
"unsafe"
)
func main() {
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
sizeOfIntInBits := bits.UintSize
fmt.Printf("%d bits\n", sizeOfIntInBits)
var a int
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
b := 2
fmt.Printf("b's typs is %s\n", reflect.TypeOf(b))
}
Output:
64 bits
8 bytes
a's type is int
b's typs is int
int8
Kích thước: 8 bits hoặc 1 byte
Phạm vi: -2^7 đến 2^7 -1.
Khi nào sử dụng:
- Nên sử dụng int8 khi biết rằng phạm vi của số nguyên nằm trong khoảng từ -2^7 đến 2^7 -1. Đối với các giá trị tạm thời như các biến loop invariants, vẫn nên sử dụng int, ngay cả khi nó chiếm nhiều không gian hơn vì nó có thể được nâng cấp lên int trong một số thao tác hoặc cuộc gọi thư viện.
- Đối với các giá trị mảng nằm trong khoảng từ -2^7 đến 2^7 -1, việc sử dụng int8 là một trường hợp sử dụng tốt. Ví dụ, nếu bạn đang lưu trữ chỉ mục ASCII cho các chữ cái viết thường thì có thể sử dụng int8.
- Điều đó là một ý tưởng tốt để sử dụng int8 cho các giá trị dữ liệu.
int16
Kích thước: 16 bits hoặc 2 byte
Phạm vi: -2^15 to 2^15 -1.
Khi nào sử dụng:
- Nên sử dụng int16 khi biết rằng phạm vi của số nguyên nằm trong khoảng từ -2^15 đến 2^15 -1. Đối với các giá trị tạm thời như các biến loop invariants, vẫn nên sử dụng int, ngay cả khi nó chiếm nhiều không gian hơn vì nó có thể được nâng cấp lên int trong một số thao tác hoặc cuộc gọi thư viện.
- Đối với các giá trị mảng nằm trong khoảng từ -2^15 đến 2^15 -1, việc sử dụng int16 là một trường hợp sử dụng tốt. Ví dụ, nếu bạn đang lưu trữ chỉ mục ASCII cho các chữ cái viết thường thì có thể sử dụng int16.
int32
Kích thước: 32 bit or 4 byte
Phạm vi: -2^31 đến 2^31 -1.
int64
Kích thước: 64 bit hoặc 8 byte
Phạm vi:-2^63 đến 2^63 -1.
Khi nào sử dụng:
- int64 được sử dụng khi phạm vi của số nguyên là rất lớn. Ví dụ, time.Duration là kiểu int64.
UnSigned
Các số nguyên Unsigned có 5 loại như sau:
Type | Size |
uint | Phụ thuộc vào nền tảng |
uint8 | 8 bits/1 byte |
uint16 | 16 bits/2 byte |
uint32 | 32 bits/4 byte |
uint64 | 64 bits/8 byte |
uint
Kích thước: Phụ thuộc vào nền tảng.
- Trên các máy tính 32 bit, kích thước của kiểu int là 32 bit hoặc 4 byte.
- Trên các máy tính 64 bit, kích thước của kiểu int là 64 bit hoặc 8 byte.
Phạm vi: Phụ thuộc vào nền tảng
- Trên các máy tính 32 bit, phạm vi của kiểu int là từ -2^31 đến 2^31 -1.
- Trên các máy tính 64 bit, phạm vi của kiểu int là từ -2^63 đến 2^63 -1.
Khi nào nên sử dụng:
- Nên sử dụng uint trong trường hợp sử dụng số nguyên không dấu, trừ các trường hợp được đề cập dưới đây.
- Khi máy tính là 32 bit và phạm vi cần thiết lớn hơn -2^31 đến 2^31 -1, sau đó sử dụng int64 thay vì int. Lưu ý rằng trong trường hợp này, để tạo ra một số 64 bit, cần phải sử dụng 2 địa chỉ bộ nhớ 32 bit cùng nhau.
- Khi phạm vi nhỏ hơn, hãy sử dụng kiểu int thích hợp.
Thuộc tính:
- Khai báo một uint.
var a uint
- Package bits của Golang có thể giúp bạn xác định kích thước của một uint trên hệ thống của bạn.
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64sizeOfUintInBits := bits.UintSizefmt.Println(sizeOfIntInBits) => 32 or 64
- Hàm unsafe.Sizeof() cũng có thể được sử dụng để xem kích thước của một uint tính bằng byte.
Mã hoạt động đầy đủ
Dưới đây là mã đầy đủ hoạt động của các thuộc tính được đề cập ở trên.
package main
import (
"fmt"
"math/bits"
"reflect"
"unsafe"
)
func main() {
//This is computed as const uintSize = 32 << (^uuint(0) >> 32 & 1) // 32 or 64
sizeOfuintInBits := bits.UintSize
fmt.Printf("%d bits\n", sizeOfuintInBits)
var a uint
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
Output:
64 bits
8 bytes
a's type is uint
uintptr
Đây là một kiểu số nguyên không dấu có độ lớn đủ lớn để lưu trữ bất kỳ địa chỉ con trỏ nào. Do đó, kích thước và phạm vi của nó phụ thuộc vào nền tảng.
Kích thước: Phụ thuộc vào nền tảng
- Trên các máy 32 bit, kích thước của một int sẽ là 32 bit hoặc 4 byte.
- Trên các máy 64 bit, kích thước của một int sẽ là 64 bit hoặc 8 byte.
Phạm vi: Lại phụ thuộc vào nền tảng
- Trên các máy 32 bit, phạm vi của int sẽ từ -2^31 đến 2^31-1.
- Trên các máy 64 bit, phạm vi của int sẽ từ -2^63 đến 2^63-1.
Thuộc tính:
- Một uintptr có thể được chuyển đổi thành unsafe.Pointer và ngược lại.
- Có thể thực hiện phép toán số học trên uintptr.
- uintptr, mặc dù nó chứa một địa chỉ con trỏ, chỉ là một giá trị và không tham chiếu đến bất kỳ đối tượng nào. Do đó,
- Giá trị của nó sẽ không được cập nhật nếu đối tượng tương ứng di chuyển. Ví dụ: khi ngăn xếp goroutine thay đổi
- Đối tượng tương ứng có thể được thu gom rác.
Khi nào nên sử dụng:
- Mục đích của nó là được sử dụng cùng với unsafe.Pointer chủ yếu được sử dụng để truy cập bộ nhớ không an toàn.
- Khi bạn muốn lưu trữ giá trị địa chỉ con trỏ để in nó hoặc lưu trữ nó. Vì địa chỉ chỉ được lưu trữ và không tham chiếu đến bất kỳ thứ gì, đối tượng tương ứng có thể được thu gom rác.
Mã hoạt động đầy đủ
package main
import (
"fmt"
"unsafe"
)
type sample struct {
a int
b string
}
func main() {
s := &sample{a: 1, b: "test"}
//Getting the address of field b in struct s
p := unsafe.Pointer(uintptr(unsafe.Pointer(s)) + unsafe.Offsetof(s.b))
//Typecasting it to a string pointer and printing the value of it
fmt.Println(*(*string)(p))
}
Output
test
uint8
Kích thước: 8 bit hoặc 1 byte
Phạm vi: 0 đến 255 hoặc 0 đến 2^8 - 1.
Khi nào nên sử dụng:
- Sử dụng uint8 khi biết rằng phạm vi int sẽ nằm trong khoảng từ 0 đến 2^8 - 1. Đối với các giá trị tạm thời như loop invariants, vẫn nên sử dụng int mặc dù nó có thể chiếm nhiều không gian hơn vì nó có thể được thăng cấp thành int trong một số thao tác hoặc cuộc gọi thư viện.
- Đối với các giá trị mảng nằm trong khoảng từ 0 đến 28 - 1 thì uint8 là một trường hợp sử dụng tốt. Ví dụ, nếu bạn lưu trữ chỉ mục ASCII trong một mảng thì uint8 có thể được sử dụng.
uint16
Kích thước: 16 bit hoặc 2 byte
Phạm vi: 0 đến 2^16 - 1
Khi nào nên sử dụng:
- Sử dụng int16 khi biết rằng phạm vi int sẽ nằm trong khoảng từ 0 đến 2^16 - 1. Đối với các giá trị tạm thời như loop invariants, vẫn nên sử dụng int mặc dù nó có thể chiếm nhiều không gian hơn, vì nó có thể được thăng cấp thành int trong một số thao tác hoặc cuộc gọi thư viện.
- Đối với các giá trị mảng nằm trong khoảng từ -0 đến 2^16 - 1, thì int8 là một trường hợp sử dụng tốt.
uint32
Kích thước: 32 bit hoặc 4 byte
Phạm vi: 0 đến 2^32 - 1
uint64
Kích thước: 64 bit hoặc 8 byte
Phạm vi: 0 đến 2^64 - 1
Khi nào nên sử dụng:
- uint64 được sử dụng khi phạm vi là cao hơn.
Floats
Float là số có thập phân. Nó có hai loại.
Type | Size |
float32 | 32 bit hoặc 4 bytes |
float64 | 64 bit hoặc 8 bytes |
float64 là loại số thực mặc định. Khi bạn khởi tạo một biến với giá trị thập phân và không chỉ định kiểu float, kiểu mặc định được suy ra sẽ là float64.
float32
float32 sử dụng định dạng số thực độ chính xác đơn để lưu trữ giá trị. Tổng thể, đây là tập hợp của tất cả các số thực 32-bit theo chuẩn IEEE-754. 32 bit được chia thành - 1 bit dấu, 8 bit số mũ và 23 bit phần mũ. float32 chỉ chiếm một nửa kích thước của float64 và có tốc độ nhanh hơn trên một số kiến trúc máy tính.
Kích thước: 32 bit hoặc 4 byte
Phạm vi: 1.2E-38 đến 3.4E+38
Giá trị mặc định: 0.0
Khi nào nên sử dụng:
Nếu trong hệ thống của bạn bộ nhớ là một điểm hạn chế và phạm vi nhỏ hơn, thì có thể sử dụng float32.
Ví dụ:
Mã ví dụ bên dưới minh họa cho các điểm sau đây
- Khai báo một float32
- In kích thước của float32 theo byte
Code:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a float32
var a float32 = 2
//Size of float32 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
Output:
4 bytes
a's type is float32
float64
float64 là kiểu dữ liệu số thực dùng để lưu trữ giá trị với độ chính xác gấp đôi so với kiểu dữ liệu float32. Thông thường, nó được sử dụng để đại diện cho tất cả các số thực 64-bit theo chuẩn IEEE-754, bao gồm 1 bit dấu, 11 bit chỉ số mũ và 52 bit số đằng sau dấu phẩy.
Kích thước: 64-bit hoặc 8 bytes
Phạm vi: 2.2E-308 đến 1.8E+308
Giá trị mặc định: 0.0
Khi nào nên sử dụng:
Sử dụng kiểu float64 khi cần độ chính xác cao hơn so với kiểu float32.
Ví dụ:
Đoạn mã dưới đây minh họa cho việc khai báo và sử dụng kiểu float64:
- Khai báo một biến kiểu float64
- In ra kích thước của float64 theo byte
- Mặc định sẽ là kiểu float64 nếu không chỉ định kiểu dữ liệu
Code:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a float64
var a float64 = 2
//Size of float64 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
//Default is float64 when you don't specify a type
b := 2.3
fmt.Printf("b's type is %s\n", reflect.TypeOf(b))
}
Output:
8 bytes
a's type is float64
b's type is float64
Complex Numbers
Số phức có hai loại
Type | Property |
complex64 | Cả phần thực và phần ảo của số phức đều là kiểu float32. |
complex128 | Cả phần thực và phần ảo đều là float64. |
Dạng số phức mặc định là complex128
Khởi tạo
Số phức có thể được khởi tạo bằng hai cách:
- Sử dụng hàm complex. Hàm này có chữ ký như sau. Chú ý rằng cả a và b phải cùng kiểu dữ liệu, nghĩa là cả hai đều phải là float32 hoặc cả hai phải là float64.
complext(a, b)
- Sử dụng cú pháp rút gọn. Được sử dụng khi tạo số phức với các số trực tiếp. Loại số phức được tạo bằng cách sử dụng phương thức dưới đây sẽ là kiểu complex128 nếu kiểu không được chỉ định.
a := 5 + 6i
complex64
Đối với số phức 64 bit thì cả phần thực và ảo đều là float32
Kích thước: Cả phần thực và ảo đều có kích thước giống như float32. Kích thước là 32 bit hoặc 4 byte.
Phạm vi: Cả phần thực và ảo đều có phạm vi giống như float32, tức là từ 1.2E-38 đến 3.4E+38.
Ví dụ:
Dưới đây là một đoạn mã mẫu cho thấy
- Cách tạo một số phức 64 bằng hai phương pháp trên
- In kích thước của một số phức 64. Kích thước sẽ là 8 byte (4 +4) tương đương với hai số float32
- In loại của một số phức 64
- Phép toán + trên số phức
Code:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var a float32 = 3
var b float32 = 5
//Initialize-1
c := complex(a, b)
//Initialize-2
var d complex64
d = 4 + 5i
//Print Size
fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))
//Print type
fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
fmt.Printf("d's type is %s\n", reflect.TypeOf(d))
//Operations on complex number
fmt.Println(c+d, c-d, c*d, c/d)
}
Output:
c's size is 8 bytes
d's size is 8 bytes
c's type is complex64
d's type is complex64
(7+10i) (-1+0i) (-13+35i) (0.902439+0.12195122i)
complex128
Đối với complex128, cả phần thực và phần ảo đều là float64
Kích thước: Cả phần thực và phần ảo có cùng kích thước là float64. Kích thước là 64 bit hoặc 8 byte
Phạm vi: Cả phần thực và phần ảo đều có phạm vi như là float64, tức là từ -1.7E+308 đến +1.7E+308
Ví dụ:
Dưới đây là một đoạn mã mẫu thể hiện
- Cách tạo số complex128 bằng hai phương pháp trên. Nó cũng cho thấy khi không chỉ định kiểu, kiểu mặc định sẽ là complex128
- In kích thước của một số phức complex128. Kích thước sẽ là 16 byte (8 + 8) tương đương với hai số float64
- In kiểu của một số phức complex128
- Các phép toán khác nhau trên số phức.
Code:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var a float64 = 3
var b float64 = 5
//Initialize-1
c := complex(a, b)
//Initialize-2. When don't specify a type , the default type will be complex128
d := 4 + 5i
//Print Size
fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))
//Print type
fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
fmt.Printf("d's type is %s\n", reflect.TypeOf(d))
//Operations on complex number
fmt.Println(c+d, c-d, c*d, c/d)
}
Output:
c's size is 16 bytes
d's size is 16 bytes
c's type is complex128
d's type is complex128
(7+10i) (-1+0i) (-13+35i) (0.902439024390244+0.12195121951219513i)
Byte
Trong Go, byte là một bí danh cho uint8, có nghĩa là nó là một giá trị số nguyên. Giá trị số nguyên này là 8 bit và nó đại diện cho một byte, tức là số từ 0-255. Một byte đơn có thể đại diện cho các ký tự ASCII. Trong Go, không có kiểu dữ liệu 'char'. Do đó,
- byte được sử dụng để biểu thị ký tự ASCII
- rune được sử dụng để biểu thị tất cả các ký tự UNICODE bao gồm tất cả các ký tự tồn tại. Chúng ta sẽ học về rune sau trong bài học này.
Định nghĩa Byte
var rbyte byte := 'a'
Trong khi khai báo byte, chúng ta phải chỉ định kiểu, giống như trong chương trình ở trên. Nếu chúng ta không chỉ định kiểu, thì kiểu mặc định được coi là một rune.
Ví dụ
Trong ví dụ code bên dưới:
- Chúng ta khai báo một biến byte.
- In kiểu byte.
- In kích thước của byte.
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var r byte = 'a'
//Print Size
fmt.Printf("Size: %d\n", unsafe.Sizeof(r))
//Print Type
fmt.Printf("Type: %s\n", reflect.TypeOf(r))
//Print Character
fmt.Printf("Character: %c\n", r)
s := "abc"
//This will the decimal value of byte
fmt.Println([]byte(s))
}
Output:
Size: 1
Type: uint8
Character: a
[97 98 99]
Rune
Trong Go, rune là một đồng nghĩa với int32, nghĩa là nó là một giá trị số nguyên. Giá trị số nguyên này được dùng để biểu diễn một Unicode Code Point. Để hiểu về rune, bạn phải hiểu về Unicode trước. Dưới đây là mô tả ngắn gọn về Unicode, tuy nhiên bạn có thể tham khảo bài đăng phổ biến về chủ đề này - "The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)".
UniCode là gì
Unicode là một tập hợp con của các ký tự ASCII, mỗi ký tự được gán một số duy nhất được gọi là Unicode Code Point.
Ví dụ
- Chữ số 0 được biểu thị bằng điểm mã Unicode U+0030 (Giá trị thập phân - 48)
- Ký tự thường b là U+0062 (Giá trị thập phân - 98)
- Ký tự đồng bảng Anh là U+00A3 (Giá trị thập phân - 163)
Bạn có thể truy cập vào trang Wikipedia "List of Unicode characters" để biết mã Unicode Point của các ký tự khác.
UTF-8
UTF-8 lưu trữ mỗi Unicode Point bằng 1, 2, 3 hoặc 4 byte. Các điểm ASCII được lưu trữ bằng 1 byte. Đó là lý do tại sao rune là đồng nghĩa với int32 vì một Unicode Point có thể có tối đa 4 byte trong Go vì trong Go, mỗi chuỗi được mã hóa bằng utf-8.
Mỗi rune được dùng để tham chiếu tới một Unicode Point. Ví dụ, nếu bạn in một chuỗi sau khi ép kiểu nó thành một mảng rune, thì nó sẽ in ra Unicode Point của từng ký tự. Ví dụ như chuỗi "0b£" sẽ in ra - [U+0030 U+0062 U+00A3].
fmt.Printf("%U\n", []rune("0b£"))
Khai báo Rune
Một rune được khai báo bằng cách sử dụng một ký tự nằm trong dấu nháy đơn như ví dụ dưới đây để khai báo một biến có tên là 'rPound'
rPound := '£'
Sau khi khai báo Rune, bạn có thể thực hiện các thao tác sau đây:
- Print Type – Kết quả sẽ là int32
fmt.Printf("Type: %s\n", reflect.TypeOf(rPound))
- Print Unicode Code Point – Kết quả sẽ là U+00A3
fmt.Printf("Unicode CodePoint: %U\n", rPound)
- Print Character – Kết quả sẽ là £
fmt.Printf("Character: %c\n", r)
Khi nào sử dụng:
Bạn nên sử dụng rune khi bạn muốn lưu trữ Unicode Code Point trong giá trị. Một mảng rune nên được sử dụng khi tất cả các giá trị trong mảng đều là một Unicode Code Point.
Code:
Dưới đây là mã code minh họa cho mỗi điểm chúng ta đã thảo luận
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
r := 'a'
//Print Size
fmt.Printf("Size: %d\n", unsafe.Sizeof(r))
//Print Type
fmt.Printf("Type: %s\n", reflect.TypeOf(r))
//Print Code Point
fmt.Printf("Unicode CodePoint: %U\n", r)
//Print Character
fmt.Printf("Character: %c\n", r)
s := "0b£"
//This will print the Unicode Points
fmt.Printf("%U\n", []rune(s))
//This will the decimal value of Unicode Code Point
fmt.Println([]rune(s))
}
Output:
Size: 4
Type: int32
Unicode CodePoint: U+0061
Character: a
[U+0030 U+0062 U+00A3]
[48 98 163]
String
Trong Golang, string là một slice chỉ đọc của các byte. Có hai cách để khởi tạo một string:
- Sử dụng dấu ngoặc kép "" ví dụ như "this".
Trong string bằng dấu ngoặc kép, các ký tự escape sẽ được xử lý. Ví dụ, nếu chuỗi chứa "\n", khi in ra màn hình sẽ có một dòng mới.
- Sử dụng dấu nháy ngược `
ví dụ như
\` this`.
String trong dấu nháy ngược chỉ là một chuỗi thô, nó không xử lý bất kỳ escape sequences nào.
Mỗi ký tự trong một string sẽ chiếm một số byte tùy thuộc vào encoding được sử dụng. Ví dụ, trong utf-8 encoded string, mỗi ký tự sẽ chiếm từ 1-4 byte. Bạn có thể đọc về utf-8 trong bài viết nổi tiếng và cực kỳ quan trọng này: "The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)". Trong utf-8, các ký tự "a" hoặc "b" được mã hóa bằng một byte, trong khi ký tự dấu phẩy đô la "£" được mã hóa bằng hai byte. Do đó, chuỗi "ab£" sẽ đưa ra 4 byte khi bạn chuyển đổi chuỗi thành mảng byte và in nó ra màn hình.
s := "ab£"
fmt.Println([]byte(s))
Output
[48 98 194 163]
Cũng chú ý rằng khi bạn cố gắng in độ dài của chuỗi trên bằng cách sử dụng len("ab£")
, nó sẽ in ra 4 chứ không phải 3 vì nó chứa 4 byte.
Hãy lưu ý rằng range
lặp qua các chuỗi byte tạo thành mỗi ký tự, do đó với vòng lặp range
dưới đây.
for _, c := range s {
fmt.Println(string(c))
}
Kết quả sẽ là
a
b
£
Có nhiều hoạt động có thể thực hiện trên một chuỗi. Một trong số đó là ghép nối hai chuỗi. Ký hiệu ‘+’ được sử dụng để ghép nối. Hãy xem mã hoạt động đầy đủ cho tất cả những điều chúng ta đã thảo luận
Code:
package main
import (
"fmt"
)
func main() {
//String in double quotes
x := "this\nthat"
fmt.Printf("x is: %s\n", x)
//String in back quotes
y := `this\nthat`
fmt.Printf("y is: %s\n", y)
s := "ab£"
//This will print the byte sequence.
//Since character a and b occupies 1 byte each and £ character occupies 2 bytes.
//The final output will 4 bytes
fmt.Println([]byte(s))
//The output will be 4 for same reason as above
fmt.Println(len(s))
//range loops over sequences of byte which form each character
for _, c := range s {
fmt.Println(string(c))
}
//Concatenation
fmt.Println("c" + "d")
}
Output:
x is: this
that
y is: this\nthat
[97 98 194 163]
4
a
b
£
cd
Booleans
Kiểu dữ liệu là bool và có hai giá trị có thể có là true hoặc false.
Giá trị mặc định: false
Các phép toán:
- AND – &&
- OR – ||
- Negation – !
Ví dụ
Ví dụ code dưới đây cho thấy:
- Nếu không khởi tạo, giá trị mặc định là false
- Tất cả các phép toán trên bool
Code
package main
import "fmt"
func main() {
//Default value will be false it not initialized
var a bool
fmt.Printf("a's value is %t\n", a)
//And operation on one true and other false
andOperation := 1 < 2 && 1 > 3
fmt.Printf("Ouput of AND operation on one true and other false %t\n", andOperation)
//OR operation on one true and other false
orOperation := 1 < 2 || 1 > 3
fmt.Printf("Ouput of OR operation on one true and other false: %t\n", orOperation)
//Negation Operation on a false value
negationOperation := !(1 > 2)
fmt.Printf("Ouput of NEGATION operation on false value: %t\n", negationOperation)
}
Output:
a's value is false
Ouput of AND operation on one true and other false false
Ouput of OR operation on one true and other false: true
Ouput of NEGATION operation on false value: true
Kết luận
Đây là tất cả về các kiểu cơ bản trong Golang. Hy vọng bạn đã thích bài viết này. Vui lòng chia sẻ phản hồi hoặc cải tiến hoặc lỗi trong phần bình luận.