Go 언어 프로그래밍 - 문자열, 모듈과 패키지
문자열
문자의 집합
package main
import "fmt"
func main() {
poet1 := "죽는날까지 하늘을 우러러\n 글을 작성중입니다.\n 매우 긴 글입니다.\n 반가워요"
poet2 := `이걸로도 문자를 작성할 수 있어요.
쌍따음표는 한줄에 작성하지만
이 친구는 여러줄도 지원해요.
대신 이 친구는 특수문자로 행 변경같은거 \n 못해요`
fmt.Println(poet1)
fmt.Println(poet2)
}
UTF-8 문자코드
문자 1개에 1~4bytes를 갖는다.
UTF-8은 첫 시작 비트를 확인하여 총길이를 알 수 있다. (0으로 시작하면 1, 1은 2...)
문자의 길이는 1~3bytes를 갖고 앞에 1byte는 길이 확인 비트 이다.
문자열 순회
[]rune([]int32)으로 타입 변환 후 순회
혹은 range 로 순회
package main
import "fmt"
func main() {
str := "Hello 월드"
//rune 은 int32의 별칭타입이다.
//int32의 배열로 만든것이다.
arr := []rune(str)
//[]타입정의 -> 동적배열
for i := 0; i < len(arr); i++ {
fmt.Printf("타입 : %T, value : %d, 문자값 : %c\n", arr[i], arr[i], arr[i])
}
arr2 := []int32(str)
//int32와 같은지 확인
for i := 0; i < len(arr2); i++ {
fmt.Printf("타입 : %T, value : %d, 문자값 : %c\n", arr[i], arr[i], arr[i])
}
//문자 순회
for _, v := range str {
fmt.Printf("타입 : %T, value : %d, 문자값 : %c\n", v, v, v)
}
}
문자열의 합산
package main
import "fmt"
func main() {
str1 := "Hello"
str2 := "World"
str3 := str1 + " " + str2
fmt.Println(str3)
str1 += " " + str2
fmt.Println(str1)
}
문자열의 비교
==, != 같다 비교
<,>,<=,>= 대소 비교
'A'-'Z' : 65-90, 'a'-'z':97-122 사전식 비교(ASCII값 을 비교)
문자열의 구조
string 은 문자를 가지고 있는 포인터이다. 실제 문자열을 가지고 있는 공간을 가리킨다.
해당 포인터는 항상 16bytes이다.
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
str1 := "Hello 월드"
str2 := str1
stringHeader1 := (*reflect.StringHeader)(unsafe.Pointer(&str1))
stringHeader2 := (*reflect.StringHeader)(unsafe.Pointer(&str2))
fmt.Println(stringHeader1)
fmt.Println(stringHeader2)
}
문자열은 불변이다
불면(immutable)의 뜻은 문자열의 일부만을 수정할 수 없다는 것이다.
아래의 예제 두개를 진행하면, 처음 문자열의 주솟값과 slice 로 타입변환한 값의 주솟값이 다른 것을 알 수 있다.
타입변환시 값 복사가 이루어지고, 기본 문자열은 유지된다.
문자열의 합산에 있어서도 새로운 공간을 할당하고 그 위치에 값을 넣는 것이다.
기존 문자열이 변경되지 않는다.
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
str1 := "Hello 월드"
str2 := str1
stringHeader1 := (*reflect.StringHeader)(unsafe.Pointer(&str1))
stringHeader2 := (*reflect.StringHeader)(unsafe.Pointer(&str2))
fmt.Println(stringHeader1)
fmt.Println(stringHeader2)
}
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
str1 := "Hello 월드"
var slice []byte = []byte(str1)
stringHeader1 := (*reflect.StringHeader)(unsafe.Pointer(&str1))
stringHeader2 := (*reflect.StringHeader)(unsafe.Pointer(&slice))
fmt.Printf("str:\t%x\n", stringHeader1.Data)
fmt.Printf("slice:\t%x\n", stringHeader2.Data)
}
모듈
모듈의 패키지의 모음이다.
모듈의 역사
go1.11 이전에는 모듈이 없었다.
go1.16(2020년) 이후부터는 모듈이 기본사양(default)이 되었다.
패키지
패키지는 코드를 묶는 단위이다.
모든 코드는 반드시 패키지로 묶여야 한다.
프로그램
프로그램이란 실행 시작지점(main 패키지)를 포함한 패키지 이다.
샘플(따라하기)
메인패키지 외 기타 패키지, 외부패키지를 사용해보는 샘플
//https://github.com/cri-kim/BlogPractice/blob/main/golang/go-start/goproject/usepkg/program/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, 7, 8, 9, 6, 4, 4, 33, 1, 2, 3}
graph := asciigraph.Plot(data)
fmt.Println(graph)
}
패키지 외부 공개
패키지 내의 타입,전역변수, 상수, 함수, 메서드 등을 외부로 공개하여 사용할 수 있다.
대문자로 시작하면 외부공개
소문자로 시작하면 비공개
패키지 초기화
패키지가 프로그램에 포함되어 초기화될때 패키지내 init()가 한번만 호출된다.
(해당 패키지가 호출될때, init가 있다면, 가장 먼저 호출된다.)
init()를 통해서 패키지내 전역 변수를 초기화 한다.
//https://github.com/cri-kim/BlogPractice/blob/main/golang/go-start/ex16.5/ex16.5.go
package main
import (
"ex16.5/custompkg"
"ex16.5/exinit"
)
func main() {
//custom 에서 exinit의 init 이 호출됨
custompkg.PrintCustom()
//exinit 의 init 은 더이상 호출되지 않음
//init() 은 전체에서 한번 호출됨
exinit.PrintD()
}