자바스크립트를 활성화 해주세요

Tucker의 Go 언어 프로그래밍 5장 요약

 ·  ☕ 4 min read

Tucker의 Go 언어 프로그래밍스터디 요약 노트입니다.

5장. fmt 패키지를 이용한 텍스트 입출력

GUI나 웹 서버 등 다른 interface를 사용하는 app이 아니라면 기본적으로 console로 입출력이 이루어진다. go에서는 fmt 패키지를 통해 콘솔의 입출력을 처리한다.

줄바꿈 (CR, LF)

fmt.Println() 함수는 인자로 들어온 내용을 출력하고, 이후 줄바꿈을 진행한다. 이 때 줄바꿈에 대한 ASCII 코드는 플랫폼에 따라 다르다. 그런데 go에서는 compile시 알아서 해당 플랫폼에 대한 줄바꿈 ASCII 코드를 사용한다. go에서는 무조건 LF만 출력한다.

줄바꿈에 대한 ASCII 코드는 LF(Line Feed, 0x0A), CR(Carrige Return, 0x0D)가 있는데, 이는 타자기 시절의 줄바꿈 행동과 관계가 있다.

LF는 타자기의 커서 위치에서 줄(세로 방향)을 하나 아래로 내리는 행위를 의미했으며, CR은 타자기의 커서 위치를 해당 줄의 시작 부분으로 옮기는(가로 방향) 행위를 의미한다.

CR LF를 둘 다 쓰는 유명한 예외인 Windows에서 해당 문제에 대한 이슈가 제기되었다. 해당 이슈를 확인해 본 결과 CR LF 출력 문제를 지원하지 않겠다고 했다.

하지만 실제 Windows에서 실행해보면, 무조건 CR LF를 같이 출력하는 것을 확인할 수 있다.

1
2
3
4
5
6
7
8
package main

import "fmt"

func main() {
	fmt.Println("Println")
	fmt.Print("Print with \\n\n")
}
PS > .\go_newline.exe | xxd
00000000: 5072 696e 746c 6e0d 0a50 7269 6e74 2077  Println..Print w
00000010: 6974 6820 5c6e 0d0a                      ith \n..

보다시피 Println()을 호출할 때나, Print()에서 \n을 호출할 때나 무조건 CR LF를 출력하는 것을 확인할 수 있다.

Go 언어에서 구현된 Println()의 내역을 확인해보면 그냥 \n만 마지막에 출력하는 것으로 확인할 수 있다.

즉 go에서는 무조건 줄바꿈에 LF만 사용하는 것을 확인할 수 있다. 아마 CR LF 문제는 Windows에서 처리하도록 변경된 것으로 보인다. 해당 부분에 대한 조사가 필요할 것 같다.

Printf의 formatter

Printf()는 각 변수를 형식에 맞춰 출력시키는 함수다. 그리고 해당 형식을 표현하는 formatter 종류는 다양하게 존재한다.

go는 강 타입 언어로, 명시적으로 타입 변환을 해야하는데, Printf()의 인자로 들어가는 경우에는 타입을 강제로 지정하지 않는다. (interface를 인자로 받는데, 이게 타입 검사를 강제화 하지 않는 것으로 보인다.) 억지로 type에 맞지 않는 formatter를 사용해보자.

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
	var num int = 1010;

	fmt.Printf("%d %t %f %s", num, num, num, num);
}
$ ./go_printf
1010 %!t(int=1010) %!f(int=1010) %!s(int=1010)

# 빌드 과정에서 에러로 취급되지 않은 경고문을 확인
$ go vet go_printf.go
# command-line-arguments
./go_printf.go:8:2: Printf format %t has arg num of wrong type int

자체적으로 잘못된 formatter를 사용했음을 Printf()의 결과로 알린다.

입력 scan 과정의 오류

fmt.Scan() 등의 함수를 통해 입력을 받을 수 있는데, 여기도 C처럼 address를 전달한다. Call by address 방식만 지원되는 것으로 보인다.

Scan() 과정에서 원하는 타입대로 입력하지 않으면 오류가 발생한다. 이때 Error 처리를 stderr로 출력하지 않고, 해당 함수의 인자로 에러 알림을 변수로 받을 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
	var num1 int
	var num2 int

	fmt.Print("Enter 2 numbers: ")
	n, err := fmt.Scanln(&num1, &num2)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("%d numbers entered, num1: %d, num2: %d", n, num1, num2)
	}
}
$ ./go_scan
Enter 2 numbers: 3 4
2 numbers entered, num1: 3, num2: 4

# 일부러 잘못된 값을 입력해보자.
$ ./go_scan
Enter 2 numbers: hello
expected integer

추가적으로 fmt.Scanln()에서 return값을 할당하는 과정에서 볼 수 있듯, go에서는 함수에서 여러 값을 반환할 수 있는 것으로 보인다.

사용되지 않는 변수에 대한 오류

go에서는 선언하고 사용되지 않는 (reference되지 않는) 변수를 허용하지 않는다. fmt.Scanln()과 같이 여러 값을 반환하는 함수에서 일부 값만 사용하고, 나머지를 버리기 위해서는 _(blank identifier)를 사용하면 된다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
	var num1 int
	var num2 int

	fmt.Print("Enter 2 numbers: ")
	n, err := fmt.Scanln(&num1, &num2)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("num1: %d, num2: %d", num1, num2)
	}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
	var num1 int
	var num2 int

	fmt.Print("Enter 2 numbers: ")
	_, err := fmt.Scanln(&num1, &num2)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("num1: %d, num2: %d", num1, num2)
	}
}
# unref.go를 빌드
$ go build
# unref
./unref.go:10:2: n declared but not used

# blank.go를 빌드 (문제 없음)
$ go build

저자 강의


JaeSang Yoo
글쓴이
JaeSang Yoo
The Programmer

목차