갈루아의 반서재


Strings, bytes, runes and characters in Go


Go에서 문자열이란 사실 '읽기전용' 바이트 슬라이스라고 할 수 있다. 

\xNN 표기법을 사용하는 다음의 예를 보자 (바이트는 hexadecimal 값으로 00 에서 FF 까지의 값을 가진다)

1
const sample = "\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98"
cs

앞선 예제의 일부 바이트는 ASCII 나 UTF-8 이 아니다. sample 을 출력하면 다음과 같이 흉한 결과물이 나올 것이다 (환경에 따라 출력되는 모양은 다소 다를 수 있다).

1
2
3
fmt.Println(sample)

 ▒▒=▒ ⌘
cs

문자열이 어떤 값을 가지고 있는지 정확히 알아보기 위해서, 부분부분 나눠서 살펴볼 필요가 있다. 몇 가지 방법이 있는데, 루프문을 통해 바이트 각각을 뽑아내는 것이 가장 확실할 것이다. 

1
2
3
4
5
    for i := 0; i < len(sample); i++ {
 
        fmt.Printf("%x ", sample[i])
 
    }
cs

위의 코드는 문자가 아닌  바이트 단위로 접근하게 된다. 바이트 단위로 반복한 결과는 다음과 같다. 개별 바이트 값이 헥사데티미컬 값과 어떻게 매칭되는지 살펴보자. 

1
2
3
4
 
Byte loop:
bd b2 3d bc 20 e2 8c 98
 
cs

fmt.Printf 의 %x 포맷을 사용하여 좀 더 간결하게 표시할 수도 있다. 이 경우 바이트당 2자리를 차지한다.  

1
    fmt.Printf("%x\n", sample)
cs

위의 결과와 비교해보자.

1
2
3
4
 
Printf with %x:
bdb23dbc20e28c98
 
cs

% 와 x 사이에 스페이스를 두게되면 덜 헷갈리게 된다.

1
    fmt.Printf("% x\n", sample)
cs


1
2
3
4
 
Printf with % x:
bd b2 3d bc 20 e2 8c 98
 
cs

%q (quoted) 을 넣으면 문자열을 escape하여 표현하게 된다. 즉, 특수문자를 문자 그대로 표현한다. 

1
    fmt.Printf("%q\n", sample)
cs

자세히 보면 = 기호가 있는 것을 발견할 수 있다. 그리고 맨 마지막에 Unicode value U+2318 을 가지는 스웨덴 심볼 "Place of Interest" 를 발견할 수 있다. 

1
2
3
4
 
Printf with %q:
"\xbd\xb2=\xbc ⌘"
 
cs


Unicode Character Table  https://unicode-table.com/en/#miscellaneous-technical

"plus" 플래그를 %q 에 붙일 수도 있다. 이 경우는 앞선 예와 달리 Unicode 값을 출력한다. 

1
    fmt.Printf("%+q\n", sample)
cs

스웨디시 심볼은 \u 로 escape 된다.

1
2
3
4
 
Printf with %+q:
"\xbd\xb2=\xbc \u2318"
 
cs

이러한 테크닉은 문자열 컨텐츠를 디버깅할 때 매우 유용하다. 

아래는 상기 내용에 대한 코드 전체이다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main
 
import "fmt"
 
func main() {
    const sample = "\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98"
 
    fmt.Println("Println:")
    fmt.Println(sample)
 
    fmt.Println("Byte loop:")
    for i := 0; i < len(sample); i++ {
        fmt.Printf("%x ", sample[i])
    }
    fmt.Printf("\n")
 
    fmt.Println("Printf with %x:")
    fmt.Printf("%x\n", sample)
 
    fmt.Println("Printf with % x:")
    fmt.Printf("% x\n", sample)
 
    fmt.Println("Printf with %q:")
    fmt.Printf("%q\n", sample)
 
    fmt.Println("Printf with %+q:")
    fmt.Printf("%+q\n", sample)
}
cs

실행해보면 다음과 같다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
~/go/src/golang# ./golang
Println:
▒▒=▒ ⌘
Byte loop:
bd b2 3d bc 20 e2 8c 98
Printf with %x:
bdb23dbc20e28c98
Printf with % x:
bd b2 3d bc 20 e2 8c 98
Printf with %q:
"\xbd\xb2=\xbc ⌘"
Printf with %+q:
"\xbd\xb2=\xbc \u2318"
 
cs

Package fmt https://golang.org/pkg/fmt/