create account

[The Go Programming Language] 1장 튜토리얼 - 1.3 중복 줄 찾기 by modolee

View this thread on: hive.blogpeakd.comecency.com
· @modolee ·
$0.07
[The Go Programming Language] 1장 튜토리얼 - 1.3 중복 줄 찾기
![modolee_logo](https://steemitimages.com/DQmWQDjyP5d1RNW3mkCWUkDQ6yh3uDJFUZErPpaKBwKi4gM/iron_modolee.png)
안녕하세요. 개발자 모도리입니다.
**[The Go Programming Language](https://www.gopl.io/)** 라는 책으로 Go를 공부하고 있으며, 해당 책의 내용을 요약 정리해서 올리려고 합니다. 저는 [번역본](http://www.aladin.co.kr/shop/wproduct.aspx?ItemId=76703559)을 구매해서 공부하고 있습니다.

### 지난 게시물
* [[Go] Mac에서 Atom으로 Go 개발 환경 구축하기](https://steemit.com/kr-dev/@modolee/go-mac-atom-go)
* [[The Go Programming Language] 1장 튜토리얼 - 1.1 Hello, World](https://steemit.com/kr-dev/@modolee/the-go-programming-language-1-hello-world)
* [[The Go Programming Language] 1장 튜토리얼 - 1.2 커맨드라인 인수](https://steemit.com/kr-dev/@modolee/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` or `false`)
  > `%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`함수는 두 값을 반환합니다.
    1. 열린 파일(*os.File) : 다음에 Scanner에서 읽을 때 사용합니다.
    2. 내장 된 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` 함수에 분리를 원하는 문자열과 구분자를 넘기면 구분자로 구분된 문자열 슬라이스를 반환합니다.
👍  , , ,
properties (23)
authormodolee
permlinkthe-go-programming-language-1-1-3
categorykr-dev
json_metadata{"tags":["kr-dev","kr","go","golang","go-tutorial"],"image":["https://steemitimages.com/DQmWQDjyP5d1RNW3mkCWUkDQ6yh3uDJFUZErPpaKBwKi4gM/iron_modolee.png"],"links":["https://www.gopl.io/","http://www.aladin.co.kr/shop/wproduct.aspx?ItemId=76703559","https://steemit.com/kr-dev/@modolee/go-mac-atom-go","https://steemit.com/kr-dev/@modolee/the-go-programming-language-1-hello-world","https://steemit.com/kr-dev/@modolee/the-go-programming-language-1-1-2"],"app":"steemit/0.1","format":"markdown"}
created2018-09-24 02:08:33
last_update2018-09-24 02:08:33
depth0
children1
last_payout2018-10-01 02:08:33
cashout_time1969-12-31 23:59:59
total_payout_value0.056 HBD
curator_payout_value0.017 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length4,722
author_reputation285,192,678,959
root_title"[The Go Programming Language] 1장 튜토리얼 - 1.3 중복 줄 찾기"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id72,083,463
net_rshares55,336,216,881
author_curate_reward""
vote details (4)
@modolee ·
이어보기
[[The Go Programming Language] 1장 튜토리얼 - 1.4 애니메이션 GIF](https://steemit.com/kr-dev/@modolee/the-go-programming-language-1-1-4-gif)
properties (22)
authormodolee
permlinkre-modolee-the-go-programming-language-1-1-3-20181002t062123529z
categorykr-dev
json_metadata{"tags":["kr-dev"],"links":["https://steemit.com/kr-dev/@modolee/the-go-programming-language-1-1-4-gif"],"app":"steemit/0.1"}
created2018-10-02 06:21:24
last_update2018-10-02 06:21:24
depth1
children0
last_payout2018-10-09 06:21:24
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length135
author_reputation285,192,678,959
root_title"[The Go Programming Language] 1장 튜토리얼 - 1.3 중복 줄 찾기"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id72,460,323
net_rshares0