Tucker의 Go 언어 프로그래밍 책과 유튜브를 통해 학습 중입니다.
표준 입출력(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칸에 맞춰서 출력
%05d
- 빈칸에 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 |