[Golang] fmt 패키지를 이용한 표준 입출력

2021-09-29 hit count image

Golang에서 표준 입출력에 사용되는 fmt 패키지에 대해서 살펴보도록 하겠습니다.

개요

이번 블로그 포스트에서는 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를 사용하며, 입력시 에러가 발생하면 입력 버퍼를 비워줘야 한다는 것에 대해서 알게 되었습니다.

제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!

책 홍보

스무디 한 잔 마시며 끝내는 React Native 책을 출판한지 벌써 2년이 다되었네요.
이번에도 좋은 기회가 있어서 스무디 한 잔 마시며 끝내는 리액트 + TDD 책을 출판하게 되었습니다.

아래 링크를 통해 제가 쓴 책을 구매하실 수 있습니다.
많은 분들에게 도움이 되면 좋겠네요.

스무디 한 잔 마시며 끝내는 React Native, 비제이퍼블릭
스무디 한 잔 마시며 끝내는 리액트 + TDD, 비제이퍼블릭
Posts