Skip to content

Nhập xuất trong Go

go
package main

import "fmt"

func main() {
   fmt.Println("Xin chào thế giới!")
}

Ví dụ入门 đầu tiên của trang web này là xuất một chuỗi, phần này sẽ nói về cách thực hiện nhập xuất trong Go.

Mô tả tệp

go
var (
   Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
   Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
   Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)

Trong gói os có ba mô tả tệp được phơi bày bên ngoài, kiểu của chúng đều là *os.File, lần lượt là:

  • os.Stdin - Nhập chuẩn
  • os.Stdout - Xuất chuẩn
  • os.Stderr - Lỗi chuẩn

Nhập xuất trong Go đều không thể rời xa chúng.

Xuất

Trong Go có rất nhiều phương pháp xuất, dưới đây là một vài phương pháp phổ biến

stdout

Vì xuất chuẩn bản thân nó là một tệp, nên bạn có thể trực tiếp ghi chuỗi vào xuất chuẩn

go
package main

import "os"

func main() {
  os.Stdout.WriteString("xin chào thế giới!")
}

print

Go có hai hàm tích hợp print, println, chúng sẽ xuất tham số vào lỗi chuẩn, chỉ dùng để gỡ lỗi, thường không khuyến nghị sử dụng.

go
package main

func main() {
  print("xin chào thế giới!\n")
  println("xin chào thế giới")
}

fmt

Cách dùng phổ biến nhất là sử dụng gói fmt, nó cung cấp hàm fmt.Println, hàm này mặc định sẽ xuất tham số vào xuất chuẩn.

go
package main

import "fmt"

func main() {
  fmt.Println("xin chào thế giới!")
}

Tham số của nó hỗ trợ bất kỳ kiểu nào, nếu kiểu thực hiện interface String cũng sẽ gọi phương thức String để lấy biểu diễn chuỗi của nó, nên nội dung mà nó xuất có khả năng đọc khá cao, phù hợp với hầu hết các tình huống, nhưng do bên trong dùng đến phản xạ, trong các tình huống nhạy cảm về hiệu năng không khuyến nghị sử dụng nhiều.

bufio

bufio cung cấp phương pháp xuất có thể đệm, nó sẽ trước tiên ghi dữ liệu vào bộ nhớ, tích lũy đến một ngưỡng nhất định rồi xuất vào Writer được chỉ định, kích thước đệm mặc định là 4KB. Khi IO tệp, IO mạng khuyến nghị sử dụng gói này.

go
func main() {
  writer := bufio.NewWriter(os.Stdout)
  defer writer.Flush()
  writer.WriteString("xin chào thế giới!")
}

Bạn cũng có thể kết hợp nó với gói fmt để sử dụng

go
func main() {
  writer := bufio.NewWriter(os.Stdout)
  defer writer.Flush()
  fmt.Fprintln(writer, "xin chào thế giới!")
}

Định dạng

Chức năng xuất định dạng trong Go về cơ bản do hàm fmt.Printf cung cấp, nếu bạn đã học ngôn ngữ hệ C, chắc chắn sẽ cảm thấy rất quen thuộc, dưới đây là một ví dụ đơn giản.

go
func main() {
  fmt.Printf("xin chào thế giới, %s!", "jack")
}

Dưới đây là tất cả các động từ định dạng hiện có trong Go.

0Định dạngMô tảKiểu nhận
1%%Xuất dấu phần trăm %Bất kỳ
2%sXuất giá trị string/[] bytestring,[] byte
3%qĐịnh dạng chuỗi, chuỗi xuất ra có dấu nháy kép "" ở hai đầustring,[] byte
4%dXuất giá trị số nguyên thập phânSố nguyên
5%fXuất số dấu phẩy độngSố dấu phẩy động
6%eXuất dạng ký hiệu khoa học, cũng có thể dùng cho số phứcSố dấu phẩy động
7%EGiống với %eSố dấu phẩy động
8%gTự động phán đoán xuất %f hoặc %e theo tình hình thực tế, sẽ loại bỏ số 0 thừaSố dấu phẩy động
9%bXuất biểu diễn nhị phân của số nguyênSố
10%#bXuất biểu diễn nhị phân hoàn chỉnhSố
11%oXuất biểu diễn bát phân của số nguyênSố nguyên
12%#oXuất biểu diễn bát phân hoàn chỉnh của số nguyênSố nguyên
13%xXuất biểu diễn thập lục phân chữ thường của số nguyênSố
14%#xXuất biểu diễn thập lục phân chữ thường hoàn chỉnh của số nguyênSố
15%XXuất biểu diễn thập lục phân chữ hoa của số nguyênSố
16%#XXuất biểu diễn thập lục phân chữ hoa hoàn chỉnh của số nguyênSố
17%vXuất giá trị theo dạng ban đầu, thường dùng cho xuất cấu trúc dữ liệuBất kỳ
18%+vXuất struct sẽ thêm tên trườngBất kỳ
19%#vXuất giá trị theo định dạng cú pháp Go hoàn chỉnhBất kỳ
20%tXuất giá trị booleanBoolean
21%TXuất giá trị kiểu ngôn ngữ Go tương ứngBất kỳ
22%cXuất ký tự tương ứng với mã Unicodeint32
23%UXuất mã Unicode tương ứng với ký tựrune,byte
24%pXuất địa chỉ mà con trỏ trỏ đếnCon trỏ

Sử dụng fmt.Sprintf hoặc fmt.Printf để định dạng chuỗi hoặc xuất chuỗi định dạng, xem một vài ví dụ

go
fmt.Printf("%%%s\n", "xin chào thế giới")

fmt.Printf("%s\n", "xin chào thế giới")
fmt.Printf("%q\n", "xin chào thế giới")
fmt.Printf("%d\n", 2<<7-1)

fmt.Printf("%f\n", 1e2)
fmt.Printf("%e\n", 1e2)
fmt.Printf("%E\n", 1e2)
fmt.Printf("%g\n", 1e2)

fmt.Printf("%b\n", 2<<7-1)
fmt.Printf("%#b\n", 2<<7-1)
fmt.Printf("%o\n", 2<<7-1)
fmt.Printf("%#o\n", 2<<7-1)
fmt.Printf("%x\n", 2<<7-1)
fmt.Printf("%#x\n", 2<<7-1)
fmt.Printf("%X\n", 2<<7-1)
fmt.Printf("%#X\n", 2<<7-1)

type person struct {
    name    string
    age     int
    address string
}
fmt.Printf("%v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%+v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%#v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%t\n", true)
fmt.Printf("%T\n", person{})
fmt.Printf("%c%c\n", 20050, 20051)
fmt.Printf("%U\n", '')
fmt.Printf("%p\n", &person{})

Khi sử dụng hệ cơ số khác, thêm một khoảng trắng giữa % và động từ định dạng là có thể đạt hiệu quả dấu phân cách, ví dụ

go
func main() {
  str := "abcdefg"
  fmt.Printf("%x\n", str)
  fmt.Printf("% x\n", str)
}

Kết quả xuất của ví dụ này là

61626364656667
61 62 63 64 65 66 67

Khi sử dụng số, cũng có thể tự động bổ sung số 0. Ví dụ

go
fmt.Printf("%09d", 1)
// 000000001

Nhị phân cũng tương tự

go
fmt.Printf("%09b", 1<<3)
// 000001000

Tình huống lỗi

Số lượng ký tự định dạng < số lượng tham số

go
fmt.Printf("", "") //%!(EXTRA string=)

Số lượng ký tự định dạng > số lượng tham số

go
fmt.Printf("%s%s", "") //%!s(MISSING)

Kiểu không khớp

go
fmt.Printf("%s", 1) //%!s(int=1)

Thiếu động từ định dạng

go
fmt.Printf("%", 1) // %!(NOVERB)%!(EXTRA int=1)

Nhập

Dưới đây giới thiệu các phương pháp nhập phổ biến

read

Bạn có thể đọc nội dung nhập giống như đọc tệp trực tiếp, như sau

go
func main() {
  var buf [1024]byte
  n, _ := os.Stdin.Read(buf[:])
  os.Stdout.Write(buf[:n])
}

Cách dùng này quá phiền phức, thường không khuyến nghị sử dụng.

fmt

Chúng ta có thể sử dụng một vài hàm do gói fmt cung cấp, dùng gần giống như C.

go
// Quét văn bản đọc từ os.Stdin, phân cách theo khoảng trắng, xuống dòng cũng được coi là khoảng trắng
func Scan(a ...any) (n int, err error)

// Tương tự như Scan, nhưng gặp xuống dòng thì dừng quét
func Scanln(a ...any) (n int, err error)

// Quét theo chuỗi định dạng
func Scanf(format string, a ...any) (n int, err error)

Đọc hai số

go
func main() {
  var a, b int
  fmt.Scanln(&a, &b)
  fmt.Printf("%d + %d = %d\n", a, b, a+b)
}

Đọc mảng độ dài cố định

go
func main() {
  n := 10
  s := make([]int, n)
  for i := range n {
    fmt.Scan(&s[i])
  }
  fmt.Println(s)
}
1 2 3 4 5 6 7 8 9 10
[1 2 3 4 5 6 7 8 9 10]

bufio

Khi có một lượng lớn nhập cần đọc, khuyến nghị sử dụng bufio.Reader để đọc nội dung

go
func main() {
    reader := bufio.NewReader(os.Stdin)
    var a, b int
    fmt.Fscanln(reader, &a, &b)
    fmt.Printf("%d + %d = %d\n", a, b, a+b)
}

scanner

bufio.Scanner tương tự như bufio.Reader, nhưng nó đọc theo dòng.

go
func main() {
  scanner := bufio.NewScanner(os.Stdin)
  for scanner.Scan() {
    line := scanner.Text()
    if line == "exit" {
      break
    }
    fmt.Println("scan", line)
  }
}

Kết quả như sau

first line
scan first line
second line
scan second line
third line
scan third line
exit

TIP

Về mặt nhập xuất, nếu muốn luyện tập, hãy làm một vài bài toán thuật toán chế độ ACM đơn giản trên Luogu là có thể làm quen.

Golang by www.golangdev.cn edit