Doodly IT
[Go 묘공단 스터디] 문자열 본문
1) 문자열
-> 문자열은 " "이나 ` 로 표현한다. "은 개행문자들을 반영하지만 ` 은 반영하지않는다.
package main
import "fmt"
func main() {
poet2 := "Hello World"
poet1 := `Hello World!
Program to work and not to feel. Not even sure that this is real`
fmt.Println(poet1)
fmt.Println(poet2)
}
2) UTF-8
Go는 UTF-8을 표준 문자코드로 사용한다. UTF-8이 모든 문자를 2~3바이트로 표현한 것과 달리 자주 사용되는 영문자와 숫자를 1바이트로 표현하고 나머지 문자들을 2~3바이트로 표현한다. UTF-8은 ANSI와 1:1대응이 가능하기 때문에 바로 변환이 가능하다.
package main
import "fmt"
func main() {
var char rune = '한'
fmt.Println(char)
fmt.Printf("%c\n", char)
}
Printf와 Print함수의 차이점은 Printf은 %c처럼 출력의 형태(Format)을 정할 수 있다.
예를 들어 위에 예시 처럼 %c면 char형태로 출력하기 때문에 '한'이라고 출력하게 된다.
하지만 형태를 지정하지 않은 print함수는 54260로 숫자로 나타나게 된다.
3) 문자열 길이
문자열의 길이는 len함수를 쓴다.
package main
import "fmt"
func main() {
str1 := "Hello World"
fmt.Printf("len(str1) = %d \n", len(str1))
}
4) []rune 타입
rune형태의 배열이다.
package main
import "fmt"
func main() {
str := "Hello World"
//runes := []rune{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100}
runes := []rune(str)
fmt.Println(str)
fmt.Println(runes)
}
이때 runes의 결과는 Hello World의 UTF-8의 숫자형태로 나오게 된다.
5) 문자열 순회
문자열을 순회하는 3가지 방법이 있다.
1. 인덱스를 사용해서 바이트 단위로 순회하기
package main
import "fmt"
func main() {
str := "Hello World 은 안녕세상아"
for i := 0; i < len(str); i++ {
fmt.Printf("타입: %T, 값:%d , 문자값: %c \n ", str[i], str[i], str[i])
}
}
이 때 한글은 2~3바이트여서 str의 기본 단위인 바이트 단위로 순회하면 깨지게 된다.
2. []rune타입으로 타입 변환 후 한 글자씩 순회하기
[]rune 각 글자들로 이루어진 배열로 변환하기 때문에 한글이 깨지지 않는다.
package main
import "fmt"
func main() {
str := "Hello World 은 안녕세상아"
arr := []rune(str)
for i := 0; i < len(str); i++ {
fmt.Printf("타입: %T, 값:%d , 문자값: %c \n ", arr[i], arr[i], arr[i])
}
}
3. range키워드를 이용해서 한 글자씩 순회하기
package main
import "fmt"
func main() {
str := "Hello World 은 안녕세상아"
for _, v := range str {
fmt.Printf("타입: %T 값: %d 문자값: %c \n", v, v, v)
}
}
6) 문자열 연산 & 비교
문자열도 숫자처럼 비교가 가능하다. str의 가장 작은 인덱스에서 부터 연산을 시작한다.
알파벳 순서대로 문자의 정수값은 증가한다.
package main
import "fmt"
func main() {
str := "HelloWorld"
str1 := "HelloWorld"
str2 := "NotHelloWorld"
fmt.Printf("str and str1 is %v\n", str == str1)
fmt.Printf("str and str2 is %v\n", str == str2)
fmt.Printf("str is upper than str2? %v", str < str2)//H는 N보다 더 정수값이 작기 때문에 더 앞서있다.
}
문자열도 합칠 수 있다.
package main
import "fmt"
func main() {
str := "Hello"
str1 := "World"
fmt.Print(str + str1)
}
7) 문자열은 불변이다.
package main
import "fmt"
func main() {
str := "HelloWorld"
str[1] = 'a'
fmt.Println(str)
}
실제로 위와 같은 문자열에서 1번째 인데스 값만 바꾸려고 할때 컴파일 에러가 일어난다.
그렇다고 문자열을 아예 바꿀 수 없는 것은 아니다.
package main
import "fmt"
func main() {
str := "HelloWorld"
var slice []byte = []byte(str)
slice[1] = 'a'
fmt.Println(str)
fmt.Printf("%c", slice)
}
위와 같이 바이트 단위로 변환을 하고 해당 Slice의 값만 바꾸면 된다.
위에 코드 실행 시키면 아래와 같이 e->a로 바뀌게 된다.
근데 코드를 보면 str의 값은 바뀌지 않았다는 것을 알 수 있다.
string 타입은 실제로 실제 문자가 들어 있는 포인터값과 int형으로 이루어진 자료구조기 때문에 위에 변환을 하면서 해당 포인터값의 데이터를 복사했을 뿐 slice와 str은 별개의 변수이다.
>> string형의 자료구조
type StringHeader struct{
Data uintptr
Len int
}
이를 확인하기 위해선 아래 코드로 str와 slice의 포인터 값을 얻어서 확인 할 수 있다.
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
str := "HelloWorld"
var slice []byte = []byte(str)
slice[1] = 'a'
stringheader := (*reflect.StringHeader)(unsafe.Pointer(&str))
sliceheader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
fmt.Println(stringheader)
fmt.Println(sliceheader)
}
위 코드를 실행 시키면 아래와 같이 결과가 나온다.
이렇게 str과 slice는 아예 다른 메모리에 저장이 되어있고 별개인걸 알 수 있다.
'프로그래밍 > Go' 카테고리의 다른 글
[Go 묘공단 스터디] 패키지 <작성중> (0) | 2023.11.22 |
---|---|
[Go 묘공단 스터디] Up Down 게임 (1) | 2023.11.22 |
[Go 묘공단 스터디] 포인터 (0) | 2023.11.15 |
[Go 묘공단 스터디] 구조체 (0) | 2023.11.15 |
[Go 묘공단 스터디]배열 (0) | 2023.11.15 |