안녕하세요. 개발자 모도리입니다.
The Go Programming Language 라는 책으로 Go를 공부하고 있으며, 해당 책의 내용을 요약 정리해서 올리려고 합니다. 저는 번역본을 구매해서 공부하고 있습니다.
지난 게시물
- [Go] Mac에서 Atom으로 Go 개발 환경 구축하기
- [The Go Programming Language] 1장 튜토리얼 - 1.1 Hello, World
- [The Go Programming Language] 1장 튜토리얼 - 1.2 커맨드라인 인수
튜토리얼을 너무 자세히 분석하고 있는 것 같아서, 이제부터는 뒤에서 다루지 않는 내용들만 집고 넘어가겠습니다.
1장 튜토리얼
1.3 중복 줄 찾기
- 대부분의 파일 복사, 인쇄, 검색, 정렬, 카운트 등을 수행하는 프로그램은 구조가 유사합니다.
- 입력을 순회하고, 각 원소를 계산하며, 그때 그때 또는 마지막에 결과를 생성합니다.
- 유닉스 uniq 명령과 비슷한 프로그램을 작성해 봅니다.
중복 줄 찾기 구현1
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
counts := make(map[string]int)
input := bufio.NewScanner(os.Stdin)
for input.Scan() {
counts[input.Text()]++
}
// NOTE: input.Err()에서의 잠재적 오류는 무시합니다.
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s\n", n, line)
}
}
}
예제코드 [ch1/dup1.go]
실행결과
$ go run ch1/dup1.go
아래 값을 직접 입력해야 됩니다.
hello
hello
hello
hi
hi
bye
bye
seeya
control + d
(윈도우는control + z
) 로 입력 종료3 hello
2 hi
2 bye
D
또는Z
가 출력될 수 있는데... 이건 입력 종료 때 입력 된 키가 겹쳐서 보이는 것입니다.
- if문 (line 17)
- 조건 절 주위에는 for문과 마찬가지로 괄호를 사용하지 않습니다.
- map 데이터 타입 (line 10, 13)
- key, value 형태로 데이터를 저장합니다.
- 예제코드 [ch1/dup1.go]에서는 key는
string
, value는int
입니다. - 저장, 추출, 맵 안에 있는 특정 원소의 유무 검사를 상수 시간에 수행합니다.
- 각 줄을 읽을 때 마다 읽은 줄(string)을 map의 key로 사용하고 key에 해당하는 값을 증가 시킵니다.
counts[input.Text()]++ // 위와 동일한 역할을 수행한다. line := input.Text() counts[line] = counts[line] + 1
- range의 범위로 map을 사용하였을 경우 index, index의 원소 값이 아니라, key, value 쌍을 반환한다.
- 4.3절에서 자세히 다룹니다.
- bufio 패키지 (line 11, 13)
- 입력과 출력을 효율적이고 편리하게 도와주는 패키지입니다.
- Scanner 타입은 입력을 읽고 줄이나 단어 단위로 나눌 때 사용합니다.
input := bufio.NewScanner(os.Stdin)
표준 입력을 읽습니다.input.Scan()
을 호출 할 때 마다 다음 줄을 읽고 맨 끝의 개행문자를 제거합니다.input.Text()
를 호출하여 결과를 얻을 수 있습니다.
- fmt.Printf 함수 (line 18)
- C나 그 외의 언어의 printf와 마찬가지로 포매팅한 결과를 출력합니다.
fmt.Printf 포매팅 옵션
%d
: 10진 정수
%x, %o, %b
: 16진, 8진, 2진 정수
%f, %g, %e
: 부동소수점 수
%t
: Boolean (true
orfalse
)
%c
: Rune 문자 (유니코드 문자열)
%s
: 문자열
%q
: 따옴표로 묶인 문자열 "abc" 또는 Rune 'c'
%v
: 원래 형태의 값
%T
: 값의 타입
%%
: % 기호 (연산자 아님)
중복 줄 찾기 구현2
- 표준 입력을 읽거나 파일명의 목록을 받아 각각
os.Open
으로 열고 처리합니다.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
counts := make(map[string]int)
files := os.Args[1:]
if len(files) == 0 {
countLines(os.Stdin, counts)
} else {
for _, arg := range files {
f, err := os.Open(arg)
if err != nil {
fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
continue
}
countLines(f, counts)
f.Close()
}
}
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s\n", n, line)
}
}
}
func countLines(f *os.File, counts map[string]int) {
input := bufio.NewScanner(f)
for input.Scan() {
counts[input.Text()]++
}
// NOTE: input.Err()에서의 잠재적 오류는 무시합니다.
}
예제코드 [ch1/dup2.go]
hello
hi
hi
hello
hello
bye
bye
seeya
입력 파일 [ch1/input/words]
실행결과
$ go run ch1/dup2.go ch1/input/words
3 hello
2 hi
2 bye
$ go run ch1/dup2.go ch1/input/no_file
dup2: open ch1/input/no_file: no such file or directory
- 파일 열기, 닫기 (line 16, 22)
os.Open
함수는 두 값을 반환합니다.- 열린 파일(*os.File) : 다음에 Scanner에서 읽을 때 사용합니다.
- 내장 된 error 타입의 값 : 값이 nil과 같으면 파일이 성공적으로 열린 것입니다.
- 파일이 성공적으로 열리지 않은 경우에는
Fprintf
를 이용해서 에러 메세지를 출력합니다. - 파일을 다 읽고 끝에 도달했다면,
Close
를 이용해서 파일을 닫고 할당 된 모든 리소스를 해제합니다.
중복 줄 찾기 구현3
- 입력 데이터 전체를 메모리로 읽어 들인 다음 한 번에 모든 줄을 분리하고 줄 단위로 처리합니다.
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
func main() {
counts := make(map[string]int)
for _, filename := range os.Args[1:] {
data, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "dup3: %v\n", err)
continue
}
for _, line := range strings.Split(string(data), "\n") {
counts[line]++
}
}
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s\n", n, line)
}
}
}
예제코드 [ch1/dup3.go]
- 파일의 모든 내용 읽기 (line 13)
ioutil.ReadFile
함수는 파일 이름을 받아서 파일 내용을 byte의 데이터와 에러 타입 쌍을 반환합니다.- string 으로 사용하기 위해 형 변환을 해줘야 합니다.
- 문자열 분리 (line 18)
strings.Split
함수에 분리를 원하는 문자열과 구분자를 넘기면 구분자로 구분된 문자열 슬라이스를 반환합니다.
이어보기
[The Go Programming Language] 1장 튜토리얼 - 1.4 애니메이션 GIF
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit