Golang - (문자가 아닌) 바이트 위치를 반환하는 strings.IndexRune 함수
golang의 경우 string 타입이 내부적으로 utf-8 인코딩을 유지하고 있는데요, 따라서 한글은 한 문자에 대해 3바이트의 공간을 차지합니다.
가령, 다음과 같이 문자열을 열거하는 경우,
val := "테스트1"
for i, ch := range val {
fmt.Printf("%d - %c\n", i, ch)
}
/* 출력 결과
0 - 테
3 - 스
6 - 트
9 - 1
*/
글자는 하나씩 매핑이 되지만, 인덱스는 0, 3, 6, 9와 같이 나옵니다. 혹은 다음과 같이 열거하게 되면,
for i:=0; i < len(val); i ++ {
fmt.Printf("%d - %c\n", i, val[i])
}
/* 출력 결과
0 - í
1 -
2 -
3 - ì
4 -
5 - ¤
6 - í
7 -
8 - ¸
9 - 1
10
*/
(대부분의 경우에서) 원치 않는 결과를 얻게 됩니다. 이러한 불균형은 rune 타입을 이용하는 것으로 해결할 수 있습니다.
val := "테스트1"
runeVal := []rune(val)
for i, ch := range runeVal {
fmt.Printf("%d - %c\n", i, ch)
}
/* 출력 결과
0 - 테
1 - 스
2 - 트
3 - 1
*/
fmt.Printf("len(val) == %d\n", utf8.RuneCountInString(val)) // len(val) == 4
여기서, 특정 문자를 찾는 것을 해볼까요? 이를 위해 golang에서는 strings.Index와 strings.IndexRune을 제공합니다.
fmt.Printf("%d\n", strings.IndexRune(val, '1'))
fmt.Printf("%d\n", strings.Index(val, "1"))
/* 출력 결과
9
9
*/
그런데, 보다시피 출력 결과가 byte 기준의 index만을 반환하고 있습니다. 즉, 위의 결과에서 3이 나올 수 있는 Index 함수가 없는 것입니다. 이로 인해 [] rune을 열거하는 for 루프 내에서 IndexRune 함수를 쓰는 것이 매우 애매해집니다.
val := "테스트1"
runeVal := []rune(val)
for i, ch := range runeVal {
// pos는 바이트 메모리의 위치이므로 runeVal의 위치와 무관
// 또한 val[i:]도 바이트를 기준으로 한 위치이므로 runeVal의 index와 무관
pos := strings.IndexRune(val[i:], '1')
}
사실 이를 위해 가장 좋은 방법은 [] rune 타입에서 Index를 제공하는 것인데 현재는 이를 제공하지 않으므로 그냥 만들어 써야 합니다.
How found offset index a string in rune using go
; https://stackoverflow.com/questions/41956391/how-found-offset-index-a-string-in-rune-using-go
val := "테스트1"
runeVal := []rune(val)
fmt.Printf("%d\n", search(runeVal, "i")) // -1
fmt.Printf("%d\n", search(runeVal, "1")) // 3
func search(text []rune, what string) int {
whatRunes := []rune(what)
for i := range text {
found := true
for j := range whatRunes {
if text[i+j] != whatRunes[j] {
found = false
break
}
}
if found {
return i
}
}
return -1
}
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]