Go 입출력
package main
import "fmt"
func main() {
fmt.Println("Hello 세계!")
}이 사이트의 첫 입문 사례는 문자열을 출력하는 것이었습니다. 이번 절에서는 Go 에서 입출력을 어떻게 수행하는지 알아보겠습니다.
파일 디스크립터
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)os 패키지 아래에는 세 개의 외부에 공개된 파일 디스크립터가 있으며, 타입은 모두 *os.File입니다. 각각 다음과 같습니다.
os.Stdin- 표준 입력os.Stdout- 표준 출력os.Stderr- 표준 오류
Go 에서 입출력은 이들을 벗어나지 않습니다.
출력
Go 에는 여러 가지 출력 방법이 있습니다. 아래는 몇 가지 일반적인 방법입니다.
stdout
표준 출력은 본질적으로 파일이므로 문자열을 표준 출력에 직접 쓸 수 있습니다.
package main
import "os"
func main() {
os.Stdout.WriteString("hello world!")
}print
Go 에는 두 개의 내장 함수 print, println이 있으며, 이들은 매개변수를 표준 오류로 출력합니다. 디버깅용으로만 사용되며 일반적으로 권장되지 않습니다.
package main
func main() {
print("hello world!\n")
println("hello world")
}fmt
가장 일반적인 용도는 fmt 패키지를 사용하는 것입니다. fmt.Println 함수를 제공하며, 이 함수는 기본적으로 매개변수를 표준 출력으로 출력합니다.
package main
import "fmt"
func main() {
fmt.Println("hello world!")
}이 함수의 매개변수는 임의의 타입을 지원하며, 타입이 String 인터페이스를 구현하면 String 메서드를 호출하여 문자열 표현을 가져옵니다. 따라서 출력 내용의 가독성이 높아 대부분의 상황에 적합합니다. 다만 내부에서 리플렉션을 사용하므로 성능이 중요한 상황에서는 대량 사용을 권장하지 않습니다.
bufio
bufio 는 버퍼링된 출력 방법을 제공합니다. 데이터를 먼저 메모리에 쓴 후 일정 임계값에 도달하면 지정된 Writer 로 출력합니다. 기본 버퍼 크기는 4KB입니다. 파일 IO, 네트워크 IO 시 이 패키지를 사용하는 것을 권장합니다.
func main() {
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
writer.WriteString("hello world!")
}fmt 패키지와 결합하여 사용할 수도 있습니다.
func main() {
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
fmt.Fprintln(writer, "hello world!")
}포맷팅
Go 의 포맷팅 출력 기능은 기본적으로 fmt.Printf 함수에서 제공합니다. C 계열 언어를 배운 적이 있다면 익숙할 것입니다. 아래는 간단한 예시입니다.
func main() {
fmt.Printf("hello world, %s!", "jack")
}아래는 Go 가 현재 지원하는 모든 포맷팅 동사입니다.
| 번호 | 포맷팅 | 설명 | 수신 타입 |
|---|---|---|---|
| 1 | %% | 퍼센트 기호 % 출력 | 임의 |
| 2 | %s | string/[] byte 값 출력 | string,[] byte |
| 3 | %q | 문자열 포맷팅, 출력 문자열 양쪽에 큰따옴표 "" | string,[] byte |
| 4 | %d | 10 진수 정수 값 출력 | 정수 |
| 5 | %f | 부동소수점 출력 | 부동 |
| 6 | %e | 과학적 표기법 형태 출력, 복소수에도 사용 가능 | 부동 |
| 7 | %E | %e 와 동일 | 부동 |
| 8 | %g | 상황에 따라 %f 또는 %e 출력, 불필요한 0 제거 | 부동 |
| 9 | %b | 정수의 2 진수 표현 출력 | 숫자 |
| 10 | %#b | 2 진수 전체 표현 출력 | 숫자 |
| 11 | %o | 정수의 8 진수 표현 출력 | 정수 |
| 12 | %#o | 정수의 전체 8 진수 표현 출력 | 정수 |
| 13 | %x | 정수의 소문자 16 진수 표현 출력 | 숫자 |
| 14 | %#x | 정수의 전체 소문자 16 진수 표현 출력 | 숫자 |
| 15 | %X | 정수의 대문자 16 진수 표현 출력 | 숫자 |
| 16 | %#X | 정수의 전체 대문자 16 진수 표현 출력 | 숫자 |
| 17 | %v | 값의 원래 형태 출력, 주로 데이터 구조 출력에 사용 | 임의 |
| 18 | %+v | 구조체 출력 시 필드명 포함 | 임의 |
| 19 | %#v | 전체 Go 문법 형식의 값 출력 | 임의 |
| 20 | %t | 불리언 값 출력 | 불리언 |
| 21 | %T | 값에 해당하는 Go 언어 타입 값 출력 | 임의 |
| 22 | %c | Unicode 코드에 해당하는 문자 출력 | int32 |
| 23 | %U | 문자에 해당하는 Unicode 코드 출력 | rune,byte |
| 24 | %p | 포인터가 가리키는 주소 출력 | 포인터 |
fmt.Sprintf 또는 fmt.Printf 를 사용하여 문자열을 포맷팅하거나 포맷팅된 문자열을 출력합니다. 몇 가지 예시를 보겠습니다.
fmt.Printf("%%%s\n", "hello world")
fmt.Printf("%s\n", "hello world")
fmt.Printf("%q\n", "hello world")
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{})다른 진법을 사용할 때는 % 와 포맷팅 동사 사이에 공백을 추가하면 구분자 효과를 얻을 수 있습니다. 예를 들어
func main() {
str := "abcdefg"
fmt.Printf("%x\n", str)
fmt.Printf("% x\n", str)
}이 예시의 출력 결과는 다음과 같습니다.
61626364656667
61 62 63 64 65 66 67숫자를 사용할 때는 자동으로 0 을 채울 수도 있습니다. 예를 들어
fmt.Printf("%09d", 1)
// 0000000012 진수도 마찬가지입니다.
fmt.Printf("%09b", 1<<3)
// 000001000오류 상황
포맷팅 문자 수 < 매개변수 목록 수
fmt.Printf("", "") //%!(EXTRA string=)포맷팅 문자 수 > 매개변수 목록 수
fmt.Printf("%s%s", "") //%!s(MISSING)타입 불일치
fmt.Printf("%s", 1) //%!s(int=1)포맷팅 동사 누락
fmt.Printf("%", 1) // %!(NOVERB)%!(EXTRA int=1)입력
다음은 일반적인 입력 방법을 소개합니다.
read
파일을 읽는 것과 마찬가지로 입력 내용을 읽을 수 있습니다. 다음과 같습니다.
func main() {
var buf [1024]byte
n, _ := os.Stdin.Read(buf[:])
os.Stdout.Write(buf[:n])
}이렇게 사용하기에는 너무 번거로우므로 일반적으로 권장되지 않습니다.
fmt
fmt 패키지에서 제공하는 몇 가지 함수를 사용할 수 있으며, 사용법은 C 와 비슷합니다.
// os.Stdin 에서 읽은 텍스트를 스캔, 공백으로 구분, 줄바꿈도 공백으로 간주
func Scan(a ...any) (n int, err error)
// Scan 과 유사하지만 줄바꿈을 만나면 스캔 중지
func Scanln(a ...any) (n int, err error)
// 포맷팅된 문자열에 따라 스캔
func Scanf(format string, a ...any) (n int, err error)두 개의 숫자 읽기
func main() {
var a, b int
fmt.Scanln(&a, &b)
fmt.Printf("%d + %d = %d\n", a, b, a+b)
}고정 길이 배열 읽기
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
대량의 입력을 읽어야 할 때는 bufio.Reader 를 사용하여 내용을 읽는 것을 권장합니다.
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 는 bufio.Reader 와 유사하지만, 줄 단위로 읽습니다.
func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := scanner.Text()
if line == "exit" {
break
}
fmt.Println("scan", line)
}
}결과는 다음과 같습니다.
first line
scan first line
second line
scan second line
third line
scan third line
exitTIP
입출력 연습을 하고 싶다면 洛谷 에서 간단한 ACM 모드 알고리즘 문제를 몇 풀면 익숙해질 수 있습니다.
