개요
이번 블로그 포스트에서는 Golang에서 기본적으로 제공하는 입출력 패키지인 fmt
에 대해서 살펴보려 합니다.
- 공식 사이트: fmt
이 블로그 포스트에서 소개하는 코드는 다음 링크를 통해 확인하실 수 있습니다.
표준 출력
Golang에서 fmt
를 사용하는 방법을 알아보기 위해 main.go
파일을 생성하고 다음과 같이 수정합니다.
package main
import "fmt"
func main() {
var a int = 10
var b int = 20
var f float64 = 3.14
fmt.Print("a: ", a);
fmt.Println("b: ", b);
fmt.Printf("a: %d / f: %f\n", a, f);
}
프로그램을 실행하면 다음과 같은 결과를 얻을 수 있습니다.
a: 10b: 20
a: 10 / f: 3.140000
각각의 표준 출력 함수는 다음과 같은 특징을 가지고 있습니다.
- Print(): 함수의 입력값들을 출력하고 개행(줄바꿈)을 하지 않습니다.
- Println(): 함수의 입력값들을 출력하고 개행(줄바꿈)합니다.
- Printf(): 포맷(Format)에 맞도록 입력값들을 출력합니다.
포맷
Printf
에서 사용되는 포맷(Format)은 다음과 같습니다.
%v
: 데이터 타입에 맞춰서 기본 형태로 출력%T
: 데이터 타입 출력%t
: bool을 true/false로 출력%d
: 정수%b
: 2진수로 출력%c
: 유니코드 문자로 출력(정수만 가능)%o
: 8진수로 출력%O
: 앞에 8진수임을 표시하는Oo
를 붙여서 출력%x
: 16진수로 값을 출력 10이상은 a-f로 표시%X
: 16진수로 값을 출력 10이상은 A-F로 표시%e
: 지수 형태로 실수값을 출력(실수만 가능)%E
: 지수 형태로 실수값을 출력(실수만 가능)%f
: 실수값을 그대로 출력(소수점 6자리까지만)%F
: 실수값을 그대로 출력%g
: 값이 큰 실수값은 지수 형태로 표시. 값이 작은 경우 그대로 표시(6자리가 넘어가면 지수 표현)%G
: 값이 큰 실수값은 지수 형태로 표시. 값이 작은 경우 그대로 표시%s
: 문자열을 출력%q
: 특수 문자 기능을 동작하지 않고 문자열 그대로 출력(ex> \n, \t)
정수는 다음과 같은 포맷을 사용하여 정렬을 사용할 수 있습니다.
%5d
: 5칸에 맞춰 출력(오른쪽 정렬)%05d
: 5칸에 맞춰 출력(0이 추가됨)%-5d
: 5칸에 맞춰 출력(왼쪽 정렬)
이를 확인하기 위해 다음과 같이 코드를 수정합니다.
func main() {
a := 1
b := 10
c := 100
d := 1000
e := 10000
fmt.Printf("a: %5d\n", a)
fmt.Printf("b: %5d\n", b)
fmt.Printf("c: %5d\n", c)
fmt.Printf("d: %5d\n", d)
fmt.Printf("e: %5d\n", e)
fmt.Println()
fmt.Printf("a: %05d\n", a)
fmt.Printf("b: %05d\n", b)
fmt.Printf("c: %05d\n", c)
fmt.Printf("d: %05d\n", d)
fmt.Printf("e: %05d\n", e)
fmt.Println()
fmt.Printf("a: %-5d\n", a)
fmt.Printf("b: %-5d\n", b)
fmt.Printf("c: %-5d\n", c)
fmt.Printf("d: %-5d\n", d)
fmt.Printf("e: %-5d\n", e)
}
이 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
a: 1
b: 10
c: 100
d: 1000
e: 10000
a: 00001
b: 00010
c: 00100
d: 01000
e: 10000
a: 1
b: 10
c: 100
d: 1000
e: 10000
표준 입력
fmt
를 사용하면 사용자의 입력을 받을 수 있습니다. main.go
파일을 다음과 같이 수정합니다.
package main
import "fmt"
func main() {
var a int
var b int
n, err := fmt.Scanln(&a, &b)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(n, a, b)
}
}
작성한 프로그램을 실행하면 다른 프로그램들과 다르게 프로그램이 종료되지 않고, 커서가 화면에 표시되는 것을 확인할 수 있습니다. 그럼 다음과 같이 입력해 봅니다.
10 30
그럼 다음과 같은 결과가 표시되는 것을 확인할 수 있습니다.
2 10 30
Scanln
는 사용자로부터 입력받은 값을 파라메터로 전달받은 메모리 주소에 저장하고, 입력 받은 갯수와 에러가 있는 경우, 에러를 반환합니다.
이 밖에 사용자의 입력을 받기 위한 fmt
함수는 다음과 같습니다.
Scan()
: 표준 입력에서 값을 입력 받는다.Scanf()
: 표준 입력에서 포맷(Format) 형태로 값을 입력 받는다.Scanln()
: 표준 입력에서 한 줄을 읽어서 값을 입력 받는다.
Scan
으로 변수를 전달할 때, &
을 사용하여 메모리 주소값을 전달합니다. Scan
은 이렇게 전달받은 주소에 사용자로부터 입력받은 값을 저장하게 됩니다.
입력 버퍼 지우기
만약 다음과 같이 사용자의 입력을 두번 받는 프로그램이 있다고 가정해 봅시다.
package main
import "fmt"
func main() {
var a int
var b int
n, err := fmt.Scanln(&a, &b)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(n, a, b)
}
n, err = fmt.Scanln(&a, &b)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(n, a, b)
}
}
해당 프로그램을 실행하고 다음과 같이 입력해 봅니다.
3 a
그러면 입력값과 해당 값을 저장할 수 있는 변수의 타입이 다르기 때문에 런타임 에러가 발생하게 됩니다.
expected integer
하지만, 두번 입력을 받도록 프로그램을 작성했지만, 두번째 입력은 실행이되지 않고 에러가 발생하여 종료되었습니다.
unexpected newline
이것은 처음 에러가 발생하였을 때, 에러가 발생한 시점 이후에 내용이 아직 버퍼에 저장되어 있고, 버퍼의 내용이 그대로 사용되면서 문제가 발생하였습니다.
이와 같은 문제를 해결하기 위해서는, 다음과 에러가 발생한 경우, 버퍼를 초기화해 줄 필요가 있습니다.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
stdin := bufio.NewReader(os.Stdin)
var a int
var b int
n, err := fmt.Scanln(&a, &b)
if err != nil {
fmt.Println(err)
stdin.ReadString('\n')
} else {
fmt.Println(n, a, b)
}
n, err = fmt.Scanln(&a, &b)
if err != nil {
fmt.Println(err)
stdin.ReadString('\n')
} else {
fmt.Println(n, a, b)
}
}
에러가 발생하면, stdin.ReadString('\n')
을 사용하면 입력 버퍼에서 개행 문자가 나올때 까지 버퍼의 내용을 읽으므로, 버퍼를 비워줄 수 있습니다.
완료
이것으로 Golang의 표준 입출력에 사용되는 fmt
패키지에 대해서 살펴보았습니다. 입력시에는 Scan
을 이용하고 출력시에는 Print
를 사용하며, 입력시 에러가 발생하면 입력 버퍼를 비워줘야 한다는 것에 대해서 알게 되었습니다.
제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!
앱 홍보
Deku
가 개발한 앱을 한번 사용해보세요.Deku
가 개발한 앱은 Flutter로 개발되었습니다.관심있으신 분들은 앱을 다운로드하여 사용해 주시면 정말 감사하겠습니다.