// 예제 1.
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)
}
---
Hello 'World'
Go is "awesome"!\\nGo is simple and\\t'powerful'
// 예제 2.
package main
import "fmt"
func main() {
// 큰따옴표에서 여러 줄을 표현하려면 \\n을 사용해야 합니다.
poet1 := "죽는 날까지 하늘을 우러러\\n한 점 부끄럼이 없기를,\\n잎새에 이는 바람에도\\n나는 괴로워했다.\\n"
// 백쿼트에서는 여러 줄 표현에 특수 문자가 필요 없습니다.
poet2 := `죽는 날까지 하늘을 우러러
한 점 부끄럼이 없기를,
잎새에 이는 바람에도
나는 괴로워했다.`
fmt.Println(poet1)
fmt.Println(poet2)
}
---
죽는 날까지 하늘을 우러러
한 점 부끄럼이 없기를,
잎새에 이는 바람에도
나는 괴로워했다.
죽는 날까지 하늘을 우러러
한 점 부끄럼이 없기를,
잎새에 이는 바람에도
나는 괴로워했다.
따라서 Go는 별다른 변환없이 한글이나 한자등을 사용할 수 있다.
UTF-8은 한 글자가 1~3 Byte 크기이기 때문에 UTF-8 문자값을 가지려면 최소 3 Byte가 필요하다. 그러나 Go언어의 기본 타입에서는 3 Byte의 정수타입이 제공되지 않는다.
따라서 4 Byte 정수 타입인 rune 타입을 사용한다.
type rune int32
package main
import "fmt"
func main() {
var char rune = '한'
fmt.Printf("%T\\n", char) // ❶ char 타입 출력
fmt.Println(char) // ❷ char값 출력
fmt.Printf("%c\\n", char) // ❸ 문자 출력
}
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)) // 영문 문자열 크기
}
---
len(str1) = 15
len(str2) = 5
한글 문자열인 str1은 크기가 15이지만 영문 문자열인 st2는 크기가 5이다.
UTF-8에서 한글은 글자당 3 Byte를 차지하기 때문이다.
영문은 1 Byte를 차지함.
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))
}
---
Hello World
Hello World
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 타입 길이
}
---
len(str) = 12
len(runes) = 8
package main
import "fmt"
func main() {
str := "Hello 월드"
arr := []rune(str)
fmt.Println("----- 인덱스를 이용한 바이트 출력 -----")
for i := 0; i < len(str); i++ {
fmt.Printf(" 타입:%T 값:%d 문자값:%c\n", str[i], str[i], str[i])
}
fmt.Println("----- 인덱스를 이용한 문자([]rune 타입) 출력 -----")
for i := 0; i < len(arr); i++ {
fmt.Printf(" 타입:%T 값:%d 문자값:%c\n", arr[i], arr[i], arr[i])
}
fmt.Println("----- range 이용한 문자 출력 -----")
for _, v := range str {
fmt.Printf(" 타입:%T 값:%d 문자값:%c\n", v, v, v)
}
}
----- 인덱스를 이용한 바이트 출력 -----
타입:uint8 값:72 문자값:H
타입:uint8 값:101 문자값:e
타입:uint8 값:108 문자값:l
타입:uint8 값:108 문자값:l
타입:uint8 값:111 문자값:o
타입:uint8 값:32 문자값:
타입:uint8 값:236 문자값:ì
타입:uint8 값:155 문자값:
타입:uint8 값:148 문자값:
타입:uint8 값:235 문자값:ë
타입:uint8 값:147 문자값:
타입:uint8 값:156 문자값:
----- 인덱스를 이용한 문자([]rune 타입) 출력 -----
타입:int32 값:72 문자값:H
타입:int32 값:101 문자값:e
타입:int32 값:108 문자값:l
타입:int32 값:108 문자값:l
타입:int32 값:111 문자값:o
타입:int32 값:32 문자값:
타입:int32 값:50900 문자값:월
타입:int32 값:46300 문자값:드
----- range 이용한 문자 출력 -----
타입:int32 값:72 문자값:H
타입:int32 값:101 문자값:e
타입:int32 값:108 문자값:l
타입:int32 값:108 문자값:l
타입:int32 값:111 문자값:o
타입:int32 값:32 문자값:
타입:int32 값:50900 문자값:월
타입:int32 값:46300 문자값:드
문자열은 +와 += 연산을 사용하여 문자열을 이을 수 있다.
//문자열 합치기
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)
}
== != 를 사용> >= < <=를 사용//문자열 비교하기
var str1 string = "ABCDE"
var str2 string = "abcde"
if str1 > str2 {
fmt.Println("str1 > str2")
} else if str1 < str2 {
fmt.Println("str1 < str2")
}
// str1 < str2
//문자열 비교하기
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)
}
// 문자열 대소 비교하기 :>, <, <=, >=
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) // ❸
}
---
BBB > aaaaAAA : false
BBB < BBAD : false
BBB <= ZZZ : true
string 타입이 가리키는 문자열의 일부만 변경할 수 없다.
var str string = "Hello World"
str = "How are you?" // ❶ 전체 바꾸기는 가능
str[2] = 'a' // ❷ Error! 일부 바꾸기는 불가능
Go 언어는 슬라이스로 타입 변환을 할 때 문자열을 복사해서 새로운 메모리 공간을 만들어 slice 가 가리키도록 하여 불변 원칙을 지킴
// 문자열 일부 변경하기
var str string = "hello world"
var runeSlice []rune = []rune(str)
fmt.Println("Original string value :", str)
strHeader := (*reflect.StringHeader)(unsafe.Pointer(&str))
fmt.Println("Value of strHeader :", strHeader)
fmt.Printf("Pointer value of strHeader : %p\n\n", strHeader)
for i, v := range runeSlice {
if v == 'o' {
runeSlice[i] = '!'
}
}
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&runeSlice))
fmt.Println("Value of sliceHeader :", sliceHeader)
fmt.Println("Size of Slice :", unsafe.Sizeof(runeSlice))
fmt.Println()
str = string(runeSlice)
fmt.Println("Changed string value :", str)
fmt.Println("Value of strHeader :", strHeader)
fmt.Printf("Pointer value of strHeader : %p\n", strHeader)
/* OUTPUT
Original string value : hello world
Value of strHeader : &{4815093 11}
Pointer value of strHeader : 0xc000010240
Value of sliceHeader : &{824633811136 11 12}
Size of Slice : 24
Changed string value : hell! w!rld
Value of strHeader : &{824633819312 11}
Pointer value of strHeader : 0xc000010240
*/
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var str string = "Hello World"
var slice []byte = []byte(str)
// ❶ unsafe와 reflect 패키지를 이용해 내부 구조체로 변환
stringheader := (*reflect.StringHeader)(unsafe.Pointer(&str))
sliceheader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
// ❷ 내부 구조체에서 가리키고 있는 메모리 주솟값을 16진수 형태로 출력
fmt.Printf("str:\t%x\n", stringheader.Data)
fmt.Printf("slice:\t%x\n", sliceheader.Data)
}
// 서로 다른 주소를 가리키고 있음
str: 10243512b
slice: 14000108ec0
strings.Builder을 이용하는 것이 효율적이다package main
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
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'))
} else {
builder.WriteRune(c)
}
}
return builder.String()
}
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)
fmt.Println(ToUpper1(str))
fmt.Println(ToUpper2(str))
}
// Hello World Welcome!
// addr1: 4953ef --> 최초의 str 변수 위치
// addr2: c000012040 --> 문자열 추가후의 str 변수 위치
// addr3: c00001a018 --> 문자열 추가후의 str 변수 위치
// HELLO WORLD WELCOME! --> 합 연산으로 생성
// HELLO WORLD WELCOME! --> strings.Builder로 생성