본문 바로가기
Language/Golang

Golang (Go언어) fmt 패키지

by HeeWorld 2024. 10. 26.

Tucker의 Go 언어 프로그래밍 책과 유튜브를 통해 학습 중입니다.

 

Golang 마스코트 Gopher(고퍼)

 

표준 입출력(Standard Input Output)

- 초기 컴퓨터는 하나의 입력장치(명령어 카드 리더기), 하나의 출력장치(프린터)로 구성이 되어있었음.

- 시간이 지나면서, 키보드로 입력하고 모니터를 통해 즉시 확인할 수 있도록 변화함.

- 입력이 되는 통로와 출력이 되는 통로는 변하지 않았고, 이것을 표준 입력/출력(bit stream/byte stream 형태)이라고 함.

- 데이터가 입력이 되고, 데이터가 출력이 되는 것. (데이터=숫자 0,1)

 

 

표준 출력 함수

- Go 언어 기본 패키지인 fmt에서 제공

   → Go 언어는 자주 사용하는 기능을 묶어서 패키지로 제공

- 패키지를 사용하려면 'import'를 사용해서 사용할 패키지를 불러와야 함.

import "fmt"

 

fmt 패키지는 3가지 표준 출력용 함수를 제공

Print() 함수 입력값들을 출력
Println() 함수 입력값들을 출력하고 개행(New line)
Printf() 서식(format)에 맞도록 입력값들을 출력

 

package main

import "fmt"

func main() {
	var a int = 10
	var b int = 20
	var f float64 = 32799438743.8297

 	fmt.Print("a:", a, "b:", b)
 	fmt.Println("a:", a, "b:", b, "f:", f)
	fmt.Printf("a: %d b: %d f:%f\n", a, b, f)
}

// 결과 값

a:10b:20a: 10 b: 20 f: 3.27994387438297e+10
a: 10 b: 20 f:32799438743.829700

 

fmt.Print("a: ", a, "b:", b)

- Print() 함수를 이용해서 기본 서식에 맞춰 표준 출력을 함.

  → 해당 함수는 출력이 끝나고 개행을 하지 않기 때문에 바로 Println() 함수 출력 결과가 이어짐

 

fmt.Println("a:", a, "b:", b, "f:", f)

- Println() 함수도 위와 동일하게 기본 서식에 맞춰서 표준 출력을 함.

  → 출력 값 사이에 공란을 삽입하고, 출력이 끝나면 개행을 하는 부분이 Print와 상이함.

  → 결과 값(a: 10 b: 20 f: 3.27994387438297e+10)을 출력하고 개행을 함.


fmt.Printf("a: %d b: %d f:%f\n", a, b, f)

- 주어진 사용자 서식에 맞춰서 입력 값을 출력함.

- 첫 번째 입력인 "a: %d b: %d f:%f\n" 이 출력 서식을 의미

  → %d (정수 값), %f (실수 값), \n (개행)

- 출력 서식에 입력 값들이 들어갈 자리를 만들고, 각 입력 값이 해당 자리에 들어감.

- Printf() 함수는 출력이 끝난 뒤 개행을 제공하지 않음.

 

 

서식문자

- Printf() 함수는 다음과 같은 형식으로 사용

Printf(서식 문자열, 인수1, 인수2, ...)

 

- 자주 사용하는 서식 문자는 "%d", "%f", "%s"이며, 어떤 서식 문자를 써야할지 모를 때는 "%v" 서식을 사용하면 기본 서식에 맞춰서 출력됨.

구분 설명
%v 데이터 타입에 맞춰서 기본 형태로 출력
%T 데이터 타입 출력
%t 불리언을 true/false로 출력
%d 10진수 정숫값으로 출력(정수 타입만 가능)
%b 2진수로 출력
%c 유니코드 문자를 출력(정수 타입만 가능)
%o 8진수로 출력
%O 앞에 8진수임을 표시하는 0o를 붙여서 8진수 값을 출력
%x 16진수로 값을 출력, 10 이상 값을 a-f 소문자로 표시
%X 16진수로 값을 출력, 10 이상 값을 A-F 대문자로 표시
%e %E 지수 형태로 실숫값을 출력(실수 타입만 가능) 
ex) -1.234456e+78
%f %F 지수 형태가 아닌 실숫값 그대로 출력(실수 타입만 가능)
ex) 123.456
%g %G 값이 큰 실숫값은 지수형태(%e)로 출력하고, 작은 실숫값은 실숫값 그대로(%f) 출력
%s 문자열을 출력
%q 특수 문자 기능을 동작하지 않고 문자열 그대로 출력
ex) fmt.Printf("%q", "hello\tWorld\n") ⇢ hello\tWorld\n
%p 메모리 주솟값을 출력

 

 

최소 출력 너비 지정

- 서식 문자를 이용해 출력 너비를 지정하고, 숫자 0으로 빈 칸을 채우고, 왼쪽 정렬을 할 수 있음.

package main

import "fmt"

func main() {
	var a = 123
	var b = 456
	var c = 123456789

	fmt.Printf("%5d, %5d\n", a, b)
	fmt.Printf("%05d, %05d\n", a, b)
	fmt.Printf("%-5d, %-05d\n", a, b)

	fmt.Printf("%5d, %5d\n", c, c)
	fmt.Printf("%05d, %05d\n", c, c)
}

// 결과 값

  123,   456
00123, 00456
123  , 456  
123456789, 123456789
123456789, 123456789

 

%5d
- 5칸에 맞춰서 출력

앞에 2칸을 띄우고 1,2,3을 넣고, 뒤에도 2칸을 띄우고 4,5,6을 넣음

 

%05d

- 빈칸에 0을 추가해서 출력(빈 공간을 띄우지말고, 빈 공간에 0을 채워서 출력)

빈칸에 0을 채워서 출력함

 

%-5d

- 좌측으로 밀어서 출력

 

%-05d

- 좌측으로 정렬해서 출력(0을 채우지 않음 =  0을 채우면 값이 달라짐)

- 큰 의미가 없음

- 는 좌측으로 밀어서 출력함

 

마지막 c는 값이 바뀌면 안되기 때문에 넘어가면 넘어가는대로 출력됨.

 

 

실수 소수점 이하 자릿수

 

%f

- 실수를 출력함

- 소수점 6자리까지 표현

 

%g

- 길이에 따라 지수 표현(6자리가 넘어가면) 또는 실수 표현(6자리가 넘어가지 않으면)으로 출력

 

%v

- 실수 타입의 기본으로 출력되는 타입이 %v 라서 6자리가 넘어가면 %v → %g로 출력되는 것

 

package main

import "fmt"

func main() {
	var a = 324.13455
	var c = 3.14

	fmt.Printf("%08.2f\n", a)
	fmt.Printf("%08.2g\n", a)
	fmt.Printf("%8.5g\n", a)
    fmt.Printf("%f\n", c)
}

// 결과 값

00324.13
03.2e+02
  324.13
3.140000

 

fmt.Printf("%08.2f\n", a)

- %08.2f는 최소 너비 8칸에 소수점 숫자는 2개만 표시하고 공란은 0으로 채우는 서식 문자


fmt.Printf("%08.2g\n", a)

- %08.2g는 총 출력되는 숫자를 2개로 제한

- 324.13455의 정수부는 324로 2개를 표현할 수 없기 때문에 지수 표현으로 전환되고, 출력되는 숫자는 총 2개만 출력해 3.2e+02로 표현

- 최소 너비 8칸이지만, 3.2e+02가 7칸을 차지해서 남은 칸에 0이 채워짐 


fmt.Printf("%8.5g\n", a)

- %8.5g는 총 출력되는 숫자를 5개로 제한

- 324.13455의 정수부 숫자는 3개이고 총 5개로 표현 가능하기 때문에 일반적인 실수 형태로 표현

- 총 출력되는 숫자는 5개로 제한되므로 324.13으로 표현되고, 앞에 0이 없기 때문에 0을 채우지 않고 두 칸을 띄움 


fmt.Printf("%f\n", c)

- %f를 하면 디폴트로 소수점 이하 숫자 6개가 표현됨 = %f는 %.6f와 같음

- 따라서, 3.140000가 출력됨

 

 

특수 문자

\n 줄바꿈
\t 탭을 삽입
\\ \ 자체를 출력
\" " 를 출력, 큰따옴표로 묶인 문자열 내부에 따옴표를 넣을 때 사용

 

package main

import "fmt"

func main() {
	str := "Hello\tGo\t\tWorld\n\"Go\"is Awesome!\n"

	fmt.Print(str)
	fmt.Printf("%s", str)
	fmt.Printf("%q", str)
}

// 결과
Hello   Go              World
"Go"is Awesome!
Hello   Go              World
"Go"is Awesome!
"Hello\tGo\t\tWorld\n\"Go\"is Awesome!\n"

 

str := "Hello\tGo\t\tWorld\n\"Go\"is Awesome!\n"

- \t : 탭

- \t\t: 탭탭

- \n: 줄바꿈

- \"Go"\: 따옴표

- \n: 줄바꿈

fmt.Print(str)

- 문자열은 기본 서식에 맞춰서 출력


fmt.Printf("%s", str)

- Printf("%s", str)과 Print(str)은 똑같이 동작

- \t를 사용해서 Hello와 Go 사이에 탭 길이 만큼 띄우고, \t\t를 하면 탭이 두 개 들어감.

- \n을 실행해 개행하고, \"으로 문자열 안에 따옴표 문자를 삽입


fmt.Printf("%q", str)

- 모든 특수문자가 기능을 잃고 문자 자체로 동작 = "Hello\tGo\t\tWorld\n\"Go\"is Awesome!\n"

 

 

표준 입력

- 표준 입력 장치에서 데이터를 얻어옴, 일반적으로 표준 입력을 변경하지 않았다면, 키보드가 표준 입력 장치

Scan() 표준 입력에서 값을 입력 받음
Scanf() 표준 입력에서 서식 형태로 값을 입력 받음
Scanln() 표준 입력에서 한 줄을 읽어서 값을 입력 받음

 

Scan()

- Scan() 함수는 값을 채워넣을 변수들의 메모리 주소를 인수로 받음

- 한 번에 여러 값을 받을 때는 변수 사이에 공란을 두어 구분(Enter 키도 공란으로 인식)

func Scan(a ...interface{}) (n int, err error)

 

package main

import "fmt"

func main() {
	var a int
	var b int

	n, err := fmt.Scan(&a, &b)
	if err != nil {
		fmt.Println(n, err)
	} else {
		fmt.Println(n, a, b)
	}
}

// 결과 값

3 4      // 정상 값을 입력했을 때
2 3 4

1 heell    // 비정상 값을 입력했을 때
1 expected integer

 

var a int, var b int

- 기본값인 0이 들어감.

 

fmt.scan(&a, &b)

- fmt.scan() 함수로  구분된 숫자 2개를 입력받아 정수 타입 변수 a,b에 채움.

 

* 참고 (&를 왜 넣어요?)

 → 변수 앞에 &를 붙이면, 변수의 메모리 주소 값을 의미함.

 → scan이 공간에 값을 넣으려면 주소값을 알아야 함. (=키보드로 부터 값을 받아서 메모리 공간에 넣어주어야 함.)

 

예를 들어 scan(a, b)를 사용하여 자동으로 변수가 들어갈 수 없냐라고 한다면 Go는 이를 지원하지 않음.

Go는 일반적으로 함수의 인자로 사용하면 기본적으로 우변(Rvalue) 형태로 쓰임.

Rvaule 형태는 우변의 형태로, a = 3이라고 했을 때 a는 좌변, 3은 우변으로 좌측은 메모리 공간 형태로 동작하고 우측에 쓰이면 값으로 쓰임.

Print(a, b) = Print(0, 0) 으로 생각하면 됨. (그래서 a, b로 쓰면 0의 값이 들어감)

scan() 함수가 메모리 주소를 알아야하는데 모르니, 주소값을 넣어줘야 하는 것.

 

if err != nil {

fmt.Println(n, err)
} else {
fmt.Println(n, a, b)

- err이 nil이 아니라면, 에러 정상 적으로 값을 받았다면 받은 값을 출력

 

결과

- 3, 4를 넣었을 때 n에 2가 입력되고(2자리), a=3, b=4가 입력됨.

- 다른 값을 넣었을 때 두 번째 값에 문자열을 입력하였기 때문에 1과 expected integer(정수를 넣어라) 가 출력됨.

 

 

입력버퍼

- buffer라고 하면, 메모리 공간을 확보하는 것이며, 입력버퍼는 임시저장소라고 생각하면 됨.

- 사용자가 표준 입력 장치로 입력 시, 입력 데이터는 컴퓨터 내부의 표준 입력 스트림이라는 메모리 공간에 임시저장됨.

- Scan() 함수들은 그 표준 입력 스트림에서 값을 읽어서 입력값을 처리함.

 

✓ 표준 임력 스트림 = 흐름

- 흐름이 들어오면 보관하고 있다가 프로그램에서 꺼내 쓰는 것(읽어 오는 것)

 

- 표준 입력

: (Bit/Byte Stream) 0과 1의 흐름으로 들어감.

 

- 표준 출력

: (Bit/Byte Stream) 0과 1의 흐름으로 출력됨.

 

var a, b int
fmt.Scanln(&a, &b)

 

사용자가 "Hello 4"를 입력하고 enter를 하면, 표준 입력 스트림에는 아래 그림과 같이 데이터가 저장됨.

- 가장 먼저 입력한 데이터부터 읽어오기 때문에 거꾸로 저장됨. (먼저 입력된 데이터가 먼저 읽히는 데이터 구조를 FIFO라고 함)

 

 

- Scan()함수는, 첫 번째 글자부터 하나씩 읽음 = 읽어봐야 숫자인지 문자인지 알 수 있음.

- 숫자가 아닌 값이 나오면 scan는 더 읽지 않고, 에러를 뱉음. = int 타입을 원했으나, 'H'라서 error

- scan가 남아있는 데이터(ello 4\n)를 다시 수행하면, 앞 글자를 또 읽어서 숫자가 아니면 에러를 뱉음.

- 따라서, 여러 번 Scan() 함수를 호출 할 때 위와 같은 문제를 해소하기 위해 입력에 실패한 경우 입력 스트림을 지워야 함.

 

스트림을 비워주는 예제:

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)
	} else {
		fmt.Println(n, a, b)
	}
}


// 출력

hello 4    // 오류 값 입력
expected integer
3 4       // 정상 값 입력
2 3 4

 

* stdin := bufio.NewReader(os.Stdin)의 := 선언대입문(선언과 대입을 한번에 한다)으로,

   var stdin = bufio.NewReader(os.Stdin)과 동일한 구문이라고 보면 됨.

   stdin 변수는 standard input의 약자로 표준 입력을 의미.

 

* n, err 변수를 선언함. (n은 보통 count, 숫자를 나타낼 때 number 약자 / err은 error의 약자로 오류를 의미할 때 사용)

 

* nil은 메모리 주소가 아무것도 아닌 주소(올바르지 않은 주소를 나타냄)

 

* stdin.ReadString('\n')은 입력 버퍼에 입력된 문자열들을 비우기 위해 다 읽어야 하는데, 해당 명령줄을 사용하면,

   stdin에서 읽어오라는 의미로 '\n'이 나올 때까지 읽으라는 의미 = 비워라, 한 번 다 읽어라.

 

* fmt.Println(n, a, b)의 n은 scanln 함수의 출력 결과의 첫 번째 결과 값 = 입력 받은 개수 (2개를 입력 받았다)

'Language > Golang' 카테고리의 다른 글

Golang (Go언어) 함수(Function)  (2) 2024.10.30
Golang (Go언어) 연산자(Operator)  (0) 2024.10.26
Golang (Go언어) 변수  (1) 2024.10.24
Golang (Go 언어) 이란?  (5) 2024.10.22
Golang (Go 언어) 설치 (Mac)  (0) 2024.10.22