15장 문자열
- Go에서 문자열의 타입명은 string이다. 문자열은 큰따옴표나 백쿼트로 묶어서 사용하는데, 백쿼트로 묶어서 사용하면 문자열안의 특수문자가 일반문자처럼 처리된다. (문자열 그대로 출력된다.)
//ch15/ex15.1/ex15.1.go
package main
import "fmt"
func main() {
// ❶ 큰따옴표로 묶으면 특수 문자가 동작합니다.
str1 := "Hello\t'World'\n"
// ❷ 백쿼트로 묶으면 특수 문자가 동작하지 않습니다.
str2 := `Go is "awesome"!\nGo is simple and\t'powerful'`
fmt.Println(str1)
fmt.Println(str2)
}
- 큰따옴표로 문자열을 묶을경우 여러줄을 표현하려면 \n을 사용해야 가능하다.
- 백쿼트에서는 여러줄표현에 특수문자가 필요없고, 여러줄로 작성하면 그대로 출력된다.
//ch15/ex15.2/ex15.2.go
package main
import "fmt"
func main() {
// 큰따옴표에서 여러 줄을 표현하려면 \n을 사용해야 합니다.
poet1 := "죽는 날까지 하늘을 우러러\n한 점 부끄럼이 없기를,\n잎새에 이는 바람에도\n나는 괴로워했다.\n"
// 백쿼트에서는 여러 줄 표현에 특수 문자가 필요 없습니다.
poet2 := `죽는 날까지 하늘을 우러러
한 점 부끄럼이 없기를,
잎새에 이는 바람에도
나는 괴로워했다.`
fmt.Println(poet1)
fmt.Println(poet2)
}
- 문자하나를 표현하는데 rune타입을 사용한다. rune타입은 int32와 같다.
- 아래의 소스를 보면, char이라는 이름의 rune타입이 있는데 , 해당 타입의 한문자를 출력하려면 %c를 사용해서 출력한다. 그냥 fmt.Println(char) 명령으로 출력하면 int32형이기때문에, 숫자값이 출력된다.
//ch15/ex15.3/ex15.3.go
package main
import "fmt"
func main() {
var char rune = '한'
fmt.Printf("%T\n", char) // ❶ char 타입 출력
fmt.Println(char) // ❷ char값 출력
fmt.Printf("%c\n", char) // ❸ 문자 출력
}
- 아래의 소스에서 한글 5글자의 길이는 15이다. UTF-8에서는 한글은 한글자당 3바이트를 차지하기때문이다.
//ch15/ex15.4/ex15.4.go
package main
import "fmt"
func main() {
str1 := "가나다라마" // ❶ 한글 문자열
str2 := "abcde" // ❷ 영문 문자열
fmt.Printf("len(str1) = %d\n", len(str1)) // 한글 문자열 크기
fmt.Printf("len(str2) = %d\n", len(str2)) // 영문 문자열 크기
}
- []rune 타입을 string형으로 변환하는 예제이다. 서로 상호 변환이 가능하다.
//ch15/ex15.5/ex15.5.go
package main
import "fmt"
func main() {
str := "Hello World"
// ❶ ‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘ ‘, ‘W’, ‘o’, ‘r’, ‘l’, ‘d’ 문자코드 배열
runes := []rune{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100}
fmt.Println(str)
fmt.Println(string(runes))
}
- string형을 []rune타입으로 타입변환을 하여, 한글을 포함한 글자수를 정확히 알수있다. 아래의 소스에서 보면 str의 길이는 12이다. 왜냐하면 한글은 한글자당 3바이트를 차지하기 때문이다.
//ch15/ex15.6/ex15.6.go
package main
import "fmt"
func main() {
str := "hello 월드" // ❶ 한글과 영문자가 섞인 문자열
runes := []rune(str) // ❷ []rune 타입으로 타입 변환
fmt.Printf("len(str) = %d\n", len(str)) // ❸ string 타입 길이
fmt.Printf("len(runes) = %d\n", len(runes)) // ➍ []rune 타입 길이
}
문자열의 순회
- 아래소스는 인덱스를 사용한 문자열순회 방법이다. 바이트단위로 순회하기때문에 한글은 깨져서 출력되는 문제가 있다.
//ch15/ex15.7/ex15.7.go
package main
import "fmt"
func main() {
str := "Hello 월드!" // ❶ 한영이 섞인 문자열
for i := 0; i < len(str); i++ { // ❶ 문자열 크기를 얻어 순회
// ❸ 바이트 단위로 출력
fmt.Printf("타입:%T 값:%d 문자값:%c\n", str[i], str[i], str[i])
}
}
- 아래 소스는 문자열을 []rune형으로 변환하여, 해당 배열을 순회하여 출력하는 소스이다. rune형은 int32타입이기때문에 영어를 포함하여, 한글을 한글자 한글자씩 []rune에 담을수있다.
//ch15/ex15.8/ex15.8.go
package main
import "fmt"
func main() {
str := "Hello 월드!" // ❶ 한영 문자가 섞인 문자열
arr := []rune(str) // ❷ 문자열을 []rune으로 형변환
for i := 0; i < len(arr); i++ { // ❸ 문자열 크기를 얻어 순회
fmt.Printf("타입:%T 값:%d 문자값:%c\n", arr[i], arr[i], arr[i])
}
}
- 아래소스는 range를 사용하여 문자열을 순회하는 방법이다. range를 사용하여 v에 한글자씩 순회한다. v는 int32형이므로 한글이 깨지지 않는다.
//ch15/ex15.9/ex15.9.go
package main
import "fmt"
func main() {
str := "Hello 월드!" // ❶ 한영 문자가 섞인 문자열
for _, v := range str { // ❷ range를 이용한 순회
fmt.Printf("타입:%T 값:%d 문자:%c\n", v, v, v) // ❸ 출력
}
}
- 아래소스는 문자열을 합치는 예제이다. + 연산자나 += 연산자를 사용하여 두 문자열을 합칠수있다.
//ch15/ex15.10/ex15.10.go
package main
import "fmt"
func main() {
str1 := "Hello"
str2 := "World"
str3 := str1 + " " + str2 //❶ str1, " ", str2를 잇습니다.
fmt.Println(str3)
str1 += " " + str2 // ❷ str1에 " " + str2 문자열을 붙입니다.
fmt.Println(str1)
}
- 아래 소스는 , 문자열의 비교할때 사용하는 연산자인 == , != 연산자에 대한 예제이다.
//ch15/ex15.11/ex15.11.go
package main
import "fmt"
func main() {
str1 := "Hello"
str2 := "Hell"
str3 := "Hello"
fmt.Printf("%s == %s : %v\n", str1, str2, str1 == str2)
fmt.Printf("%s != %s : %v\n", str1, str2, str1 != str2)
fmt.Printf("%s == %s : %v\n", str1, str3, str1 == str3)
fmt.Printf("%s != %s : %v\n", str1, str3, str1 != str3)
}
- 아래소스는 문자열을 비교하는 연산자인 < , > , <= , >= 를 사용한 예제이다.문자열의 대소비교는 문자열의 길이와 상관없이 앞글자부터 비교한다.
//ch15/ex15.12/ex15.12.go
package main
import "fmt"
func main() {
str1 := "BBB"
str2 := "aaaaAAA"
str3 := "BBAD"
str4 := "ZZZ"
fmt.Printf("%s > %s : %v\n", str1, str2, str1 > str2) // ❶
fmt.Printf("%s < %s : %v\n", str1, str3, str1 < str3) // ❷
fmt.Printf("%s <= %s : %v\n", str1, str4, str1 <= str4) // ❸
}
- string타입은 Go에서 제공하는 내장타입으로 내부구현은 감춰저 있지만, reflect패키지의 StringHeader구조체를 통해서 내부를 어느정도 엿볼수있다.
- 아래 소스처럼 str1을 str2에 대입하면 내부적으로 구조체가 복사되기때문에 같은 메모리를 가리키게 된다.
//ch15/ex15.13/ex15.13.go
package main
import "fmt"
func main() {
str1 := "안녕하세요. 한글 문자열입니다."
str2 := str1
fmt.Printf(str1) // ❷
fmt.Printf("\n")
fmt.Printf(str2) // ❷
}
- 아래의 예제를 통해서 str1과 str2가 같은 메모리주소를 가리키는걸 알수있다.
//ch15/ex15.14/ex15.14.go
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
str1 := "Hello World!"
str2 := str1 // ❶ str1 변수값을 str2에 복사
stringHeader1 := (*reflect.StringHeader)(unsafe.Pointer(&str1)) // ❷ Data값 추출
stringHeader2 := (*reflect.StringHeader)(unsafe.Pointer(&str2)) // ❸ Data값 추출
fmt.Println(stringHeader1) // ❹ 각 필드 값을 출력합니다.
fmt.Println(stringHeader2)
}
- 문자열은 불변이다. string타입이 가리키는 문자열의 일부만 변경이 불가능하다. (전체바꾸기는 가능하다.)
- 아래 소스에서 str이라는 string형의 변수가 있는데, 해당 변수를 슬라이스타입으로 바꾸어 slice에 저장하였다. 이럴경우slice는 값의 변경이 가능하다.
- 하지만, slice의 값을 바꾼다고 해서 str이 바뀌지는 않는다. 왜냐하면 서로 다른 메모리 주소를 가리키고 있기때문이다.
//ch15/ex15.15/ex15.15.go
package main
import "fmt"
func main() {
var str string = "Hello World"
var slice []byte = []byte(str) // ❶ 슬라이스로 타입 변환
slice[2] = 'a' // ❷ 3번째 문자 변경
fmt.Println(str)
fmt.Printf("%s\n", slice)
}
- 아래의 예제를 통해서 str과 slice가 서로 다른 메모리 주소를 가리키는것을 확인할수있다.
//ch15/ex15.16/ex15.16.go
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var str string = "Hello World"
var slice []byte = []byte(str)
stringheader := (*reflect.StringHeader)(unsafe.Pointer(&str)) // ❶
sliceheader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
fmt.Printf("str:\t%x\n", stringheader.Data) // ❷
fmt.Printf("slice:\t%x\n", sliceheader.Data)
}
- 아래는 문자열을 합산했을때 내부적으로 어떻게 연산이 진행되는지를 보여주는 예제이다.
- 문자열의 합연산을 하면 두문자열이 하나로 합쳐지게 된다. 이때 기존의 메모리 공간은 건드리지 않고, 새로운 메모리 공간을 만들어 두 문자열을 합치기 때문에 합연산을 하면 주소값이 변경된다.
- 아래의 addr1, addr2,addr3를 출력하면 모두 다른것을 확인할수있다.
//ch15/ex15.17/ex15.17.go
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var str string = "Hello"
stringheader := (*reflect.StringHeader)(unsafe.Pointer(&str)) // ❶
addr1 := stringheader.Data // ❷
str += " World" // ❸
addr2 := stringheader.Data // ➍
str += " Welcome!" // ➎
addr3 := stringheader.Data // ➏
fmt.Println(str)
fmt.Printf("addr1:\t%x\n", addr1)
fmt.Printf("addr2:\t%x\n", addr2)
fmt.Printf("addr3:\t%x\n", addr3)
}
- 아래 소스에서 ToUpper1()함수에서는 str크기만큼의 합연산을 하게 된다. 이럴경우 합연산을 할때마다 새로운 메모리가 할당되고, 기존 메모리 공간은 버려진다. 따라서 메모리공간낭비와 성능이 문제가 된다.
- ToUpper2()함수에서는 strings.Builder를 사용하여, WriteRune()함수를 호출할때마다 새로운 메모리를 생성하지 않고 기존메모리공간에 빈자리가 있으면 그냥 더하게 된다. 그래서 메모리 절약과 성능향상을 할수있다.
- 다시 말해서,strings.Builder는 내부적으로 가변크기의 버퍼를 사용하여 문자를 버퍼에 직접추가한다. 내부버퍼는 필요할때마다 적절한 크기로 확장되어 문자열 조합시 메모리를 효율적으로 사용할 수 있다.
//ch15/ex15.18/ex15.18.go
package main
import (
"fmt"
"strings"
)
func ToUpper1(str string) string {
var rst string
for _, c := range str {
if c >= 'a' && c <= 'z' {
rst += string('A' + (c - 'a')) // ❶ 합연산 사용
} else {
rst += string(c)
}
}
return rst
}
func ToUpper2(str string) string {
var builder strings.Builder
for _, c := range str {
if c >= 'a' && c <= 'z' {
builder.WriteRune('A' + (c - 'a')) // ❷ strings.Builder 사용
} else {
builder.WriteRune(c)
}
}
return builder.String()
}
func main() {
var str string = "Hello World"
fmt.Println(ToUpper1(str))
fmt.Println(ToUpper2(str))
}
16장 패키지
- Package는 GO에서 코드를 묶는 가장큰단위이다.
- main패키지는 특별한 패키지로 프로그램의 시작점을 포함하는 패키지이다. 그 외에 다른 여러가지 편리한 패키지가 있다. 해당 패키지를 사용하여 프로그램만들때 많은 시간을 절약할수있다.
- 아래의 Url에서 원하는 패키지를 찾을수있다.
- 아래의 Package는 Go의 표준 패키지이다.
- https://pkg.go.dev/std
- 기타 awesome-go 에서 다른 패키지를 찾을수있다.
- 아래 소스는 math/rand패키지를 사용하여 랜덤한 숫자값을 출력하는 소스이다.
//ch16/ex16.1/ex16.1.go
package main
import ( // ❶ 둘 이상의 패키지는 소괄호로 묶어줍니다.
"fmt"
"math/rand" // ❷ 패키지명은 rand입니다.
)
func main() {
fmt.Println(rand.Int()) // ❸ 랜덤한 숫자값을 출력합니다.
}
- 패키지 이름이 겹칠때는 별칭을 붙여서 해결할수있다.
- 패키지는 가져오면 반드시 사용해야한다. 사용하지 않으면 오류가 발생한다. 이럴경우 _ 을 사용해여 해결할수있다.
- Go에서 기본으로 제공하는 package는 Go를 설치할때 기본으로 같이 설치된다.
- go build를 하려면 반드시 Go 모듈 root 폴더에 go.mod파일이 있어야한다.
- go 모듈은 go mod init 명령을 통해서 만들수있다. (go mod init [패키지명])
- 아래의 소스는 go module을 만들고 외부 패키지를 활용하는 예제이다.
- go mod init goproject/usepkg 를 입력하여 go.mod파일을 생성한다. (go module파일이 만들어진다.)
- custompkg.go, usepkg.go코드를 작성한다.
- go mod tidy 를 입력하면 go 모듈에 필요한 패키지를 찾아서 다운로드 해주고, 필요한 패키지 정보를 go.mod, go.sum파일에 저장하게 된다.
- go build를 하여 exe파일을 생성하고 usepkg.exe를 실행한다.
- 아래는 해당 명령을 사용한 최종 결과파일이다.
package custompkg
import "fmt"
func PrintCustom() {
fmt.Println("this is custom package!")
}
package main
import (
"fmt"
"goproject/usepkg/custompkg"
"github.com/guptarohit/asciigraph"
"github.com/tuckersGo/musthaveGo/ch16/expkg"
)
func main() {
custompkg.PrintCustom()
expkg.PrintSample()
data := []float64{3, 4, 5, 6, 9, 7, 5, 8, 5, 10, 2, 7, 2, 5, 6}
graph := asciigraph.Plot(data)
fmt.Println(graph)
}
배열을 asciigraph 로 만드는 프로그램을 패키지를 사용하여 만들어보자.
- 아래와 같이 폴더와 파일을 준비해둔다.
- custompkg.go
package custompkg
import "fmt"
func PrintCustom() {
fmt.Println("this is custom package!")
}
- usepkg.go
package main
import (
"fmt"
"goproject/usepkg/custompkg"
"github.com/guptarohit/asciigraph"
"github.com/tuckersGo/musthaveGo/ch16/expkg"
)
func main() {
custompkg.PrintCustom()
expkg.PrintSample()
data := []float64{3, 4, 5, 6, 9, 7, 5, 8, 5, 10, 2, 7, 2, 5, 6}
graph := asciigraph.Plot(data)
fmt.Println(graph)
}
- 아래의 명령을 입력하여 go.mod파일을 생성한다.
D:\go\exam\goproject\usepkg> go mod init usepkg
- 생성된 go.mod파일에는 아래와 같이 구성되어있다.
- 하지만, asciigraph 같은 외부 패키지가 없어서 컴파일은 안된다.
module usepkg
go 1.21.4
- 아래의 명령(go mod tidy)으로 외부 패키지를 다운받는다.
D:\go\exam\goproject\usepkg> go mod tidy
go: finding module for package github.com/guptarohit/asciigraph
go: finding module for package github.com/tuckersGo/musthaveGo/ch16/expkg
go: found github.com/guptarohit/asciigraph in github.com/guptarohit/asciigraph v0.5.6
go: found github.com/tuckersGo/musthaveGo/ch16/expkg in github.com/tuckersGo/musthaveGo/ch16/expkg v0.0.0-20231030141649-380a04ad7102
D:\go\exam\goproject\usepkg>
- go.mod파일이 아래와 같이 외부 패키지를 다운로드하였다.
module usepkg
go 1.21.4
require (
github.com/guptarohit/asciigraph v0.5.6
github.com/tuckersGo/musthaveGo/ch16/expkg v0.0.0-20231030141649-380a04ad7102
)
- 다운받은 파일들은 go env를 사용하여 gopath위치에 가면 /pkg/mod/ 에서 확인할수있다.
- go build를 실행하여 exe파일을 생성한다.
- 아래 소스는 패키지파일에서 공개 여부를 결정하는 소스이다. 대문자로 시작하는 변수나 상수,함스는 공개되고, 소문자로 시작하는 변수나 함수는 비공개된다.
//ch16/ex16.2/ex16.2.go
package main
import (
"fmt"
"ch16/ex16.2/publicpkg"
)
func main() {
fmt.Println("PI:", publicpkg.PI)
publicpkg.PublicFunc()
var myint publicpkg.MyInt = 10
fmt.Println("myint:", myint)
var mystruct = publicpkg.MyStruct{Age: 18}
fmt.Println("mystruct:", mystruct)
}
// =====================================================
//ch16/ex16.6/publicpkg/publicpkg.go
package publicpkg
import "fmt"
const (
PI = 3.1415 // 외부로 공개되는 상수
pi = 3.141516 // 외부로 공개되지 않는 상수
)
var ScreenSize int = 1080 // 외부로 공개되는 변수
var screenHeight int // 외부로 공개되지 않는 변수
func PublicFunc() { // 외부로 공개되는 함수
const MyConst = 100 // 외부로 공개되지 않습니다.
fmt.Println("This is a public function", MyConst)
}
func privateFunc() { // 외부로 공개되지 않는 함수
fmt.Println("This is a private function")
}
type MyInt int // 외부로 공개되는 별칭 타입
type myString string // 외부로 공개되지 않는 별칭 타입
type MyStruct struct { // 외부로 공개되는 구조체
Age int // 외부로 공개되는 구조체 필드
name string // 외부로 공개되지 않는 구조체 필드
}
func (m MyStruct) PublicMethod() { // 외부로 공개되는 메서드
fmt.Println("This is a public method")
}
func (m MyStruct) privateMethod() { // 외부로 공개되지 않는 메서드
fmt.Println("This is a private method")
}
type myPrivateStruct struct { // 외부로 공개되지 않는 구조체
Age int // 외부로 공개되지 않는 구조체 필드
name string // 외부로 공개되지 않는 구조체 필드
}
func (m myPrivateStruct) PrivateMethod() { // 외부로 공개되지 않는 메서드
fmt.Println("This is a private method")
}
- 실행결과에서 공개된 상수와 함수,및 별칭타입을 사용할수있다.
- MyStruct는 Age,name이 있지만 name이 비공개라서 age만 출력이 된다.
- 패키지를 임포트하면, 컴파일러는 하는 일은 다음과 같다.
- 패키지내의 전역변수를 초기화한다.
- 패키지에 init()함수가 있다면 해당 함수를 호출한다. (해당 함수는 입력값과 반환값이 없어야한다.)
- 만약 해당 패키지의 init()함수만 사용하기를 원하면 _를 사용하여 임포트하면 된다.
//ch16/ex16.3/ex16.3.go
package main
import (
"ch16/ex16.3/exinit" // ⓬ exinit 패키지 임포트
"fmt"
)
func main() { // ⓭ main() 함수
fmt.Println("main function")
exinit.PrintD()
}
// ===============================================================
//ch16/exinit/exinit.go
package exinit
import "fmt"
var (
a = c + b // ❶ a값은 c와 b가 초기화된 다음 초기화됩니다.
b = f() // ❷ b값은 4가 됩니다.
c = f() // ❸ c값은 5 입니다.
d = 3 // ❹ d값은 초기화가 끝난뒤 6이 됩니다.
)
func init() { // ➎
d++ // ➏
fmt.Println("init function", d) // ➐
}
func f() int { // ➑
d++ // ➒
fmt.Println("f() d:", d) // ❿
return d // ⓫
}
func PrintD() {
fmt.Println("d:", d)
}
- fmt
- fmt.Println
- fmt.Scanln
- math
- math.Sqrt, math.Sin, math.Cos
- math.rand
- time
- time.Now, time.Date
- bufio
- os
- Http패키지
- DB 패키지
- time package Example
package main
import (
"fmt"
"time"
)
func main() {
currentTime := time.Now()
fmt.Printf("Current time: %s\n", currentTime.Format(time.RFC3339))
// 특정 시간을 생성
specificTime := time.Date(2024, time.February, 6, 12, 0, 0, 0, time.UTC)
// 두 시간 사이의 차이 계산
duration := specificTime.Sub(currentTime)
fmt.Printf("Time difference: %s\n", duration)
}
- Http Server Example
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, HTTP server!")
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Starting server on :8080...")
http.ListenAndServe(":8080", nil)
}
- Http Client Example
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
resp, err := http.Get("https://www.naver.com")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
fmt.Println(string(body))
}
- Mysql 패키지 예제
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// MySQL 데이터베이스 연결
db, err := sql.Open("mysql", "gouser:dA9!Ij@T3]@tcp(192.168.219.107:3307)/go")
if err != nil {
fmt.Println("Error opening database:", err)
return
}
defer db.Close()
// 데이터 조회
rows, err := db.Query("SELECT id, name,age,phone FROM student")
if err != nil {
fmt.Println("Error querying data:", err)
return
}
defer rows.Close()
fmt.Println("Users:")
for rows.Next() {
var id, age int
var name, phone string
err := rows.Scan(&id, &name, &age, &phone)
if err != nil {
fmt.Println("Error scanning row:", err)
return
}
fmt.Printf("ID: %d, name: %s, age: %d , phone: %s\n", id, name, age, phone)
}
}
17장 숫자맞추기 게임 만들기
- 이번장에서는 숫자맞추기 게임을 만들어보자. 규칙은 아래와 같다.
- 먼저 0~99사이의 랜덤한 숫자를 하나 정한다.
- 사용자 입력을 받는다.
- 입력값과 랜덤값을 비교한다. 사용자입력이 크면 "숫자가 큽니다."를 출력하고, 작으면 "숫자가 작습니다."를 출력하고 2번으로 가서 다시 사용자 입력을 받는다.
- 숫자값이 맞으면 "축하합니다. 시도횟수 N번"을 출력한다.
- 프로그램을 종료한다.
- 아래 소스는 해당 게임을 만들기 위한 외부모듈을 import하여 사용한 예제이다.
- math/rand의 Intn()함수를 사용하여 랜덤값을 얻는다.
- 생성되는 랜덤값은 완전랜덤값이 아닌 유사랜덤값이다. 그래서 Seed()를 사용하여 시드값을 매번 실행할때마다 다른값으로 설정한다.
- time package의 time.Now().UnixNano() 함수를 사용하여 시간값을 가져와 랜덤시드로 설정한다. 이렇게 되면 매번 다른 랜덤값이 나오게 된다.
//ch17/ex17.1/ex17.1.go
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano()) // ❶ 시간값을 랜덤 시드로 설정
n := rand.Intn(100)
fmt.Println(n)
}
rand.Seed()가 deprecated되어서 아래의 소스로 변경하였다.
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
n := rng.Intn(100)
fmt.Println(n)
}
- bufio.NewReader()로 표준입력설정
- fmt.Scanln() 함수를 사용하여 n값을 입력받는다.
- for()문에 조건이 없으므로 무한루프를 돌게 된다.
- 따라서 아래 소스는 무한히 숫자를 입력받게 되고 숫자를 입력하면 해당 숫자를 출력하는 소스이다.
//ch17/ex17.2/ex17.2.go
package main
import (
"bufio"
"fmt"
"os"
)
var stdin = bufio.NewReader(os.Stdin)
func InputIntValue() (int, error) {
var n int
_, err := fmt.Scanln(&n) // ❶ int 타입값을 입력받음
if err != nil {
stdin.ReadString('\n') // ❷ 에러 발생 시 입력스트림을 비움
}
return n, err
}
func main() {
for {
fmt.Printf("숫자값을 입력하세요:")
n, err := InputIntValue()
if err != nil {
fmt.Println("숫자만 입력하세요.")
} else {
fmt.Println("입력하신 숫자는 ", n, " 입니다.")
}
}
}
- 위의 두소스를 서로 결합하여, 아래 소스로 만들었다.
- 처음에 랜덤값을 r에 저장하고
- for문으로 무한루프를 돌면서 숫자(n)를 입력받는다.
- 입력받는 숫자가 크거나, 작으면 메세지를 출력하고 다시 입력을 받는다.
- 숫자를 맞추면 성공메세지를 출력하고 프로그램을 종료한다.
// ch17/ex17.3/ex17.3.go
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"time"
)
var stdin = bufio.NewReader(os.Stdin)
func InputIntValue() (int, error) {
var n int
_, err := fmt.Scanln(&n)
if err != nil {
stdin.ReadString('\n')
}
return n, err
}
func main() {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
r := rng.Intn(100)
// rand.Seed(time.Now().UnixNano())
// r := rand.Intn(100) // ❶ 랜덤값 생성
cnt := 1
for {
fmt.Printf("숫자값을 입력하세요:")
n, err := InputIntValue() // ❷ 숫자값 입력
if err != nil {
fmt.Println("숫자만 입력하세요.")
} else {
if n > r { // ❸ 숫자값 비교
fmt.Println("입력하신 숫자가 더 큽니다.")
} else if n < r {
fmt.Println("입력하신 숫자가 더 작습니다.")
} else {
fmt.Println("숫자를 맞췄습니다. 축하합니다. 시도한 횟수:", cnt)
break // ❹ 같을 경우 메시지를 출력하고 break로 종료
}
cnt++
}
}
}
//ch17/ex17.2/ex17.2.go
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"time"
)
const (
Balance = 1000
EarnPoint = 500
LosePoint = 100
VictoryPoint = 5000
GameoverPoint = 0
)
var stdin = bufio.NewReader(os.Stdin)
func InputIntValue() (int, error) {
var n int
_, err := fmt.Scanln(&n) // ❶ int 타입값을 입력받습니다.
if err != nil {
stdin.ReadString('\n') // ❷ 에러 발생시 입력스트림을 비웁니다.
}
return n, err
}
func main() {
rand.Seed(time.Now().UnixNano())
balance := Balance
for {
fmt.Print("1~5사이의 값을 입력하세요:")
n, err := InputIntValue()
if err != nil {
fmt.Println("숫자만 입력하세요.")
} else if n < 1 || n > 5 {
fmt.Println("1~5사이의 값만 입력하세요.")
} else {
r := rand.Intn(5) + 1
if n == r {
balance += EarnPoint
fmt.Println("축하합니다. 맞추셨습니다. 남은 돈:", balance)
if balance >= VictoryPoint {
fmt.Println("게임 승리")
break
}
} else {
balance -= LosePoint
fmt.Println("꽝 아쉽지만 다음 기회를.. 남은 돈:", balance)
if balance <= GameoverPoint {
fmt.Println("게임 오버")
break
}
}
}
}
}
'Go' 카테고리의 다른 글
[묘공단] Tucker의 Go 언어 프로그래밍 20장~21장 (0) | 2024.02.03 |
---|---|
[묘공단] Tucker의 Go 언어 프로그래밍 18장~19장 (0) | 2024.02.03 |
[묘공단] Tucker의 Go 언어 프로그래밍 12장~14장 (0) | 2024.01.29 |
[묘공단] Tucker의 Go 언어 프로그래밍 8장~11장 (0) | 2024.01.23 |
[묘공단] Tucker의 Go 언어 프로그래밍 3장~7장 (0) | 2024.01.21 |