Go에서 rune 타입은 int32의 별칭이에요.
이렇게 바탕이 되는 int32 타입 덕분에, rune 타입은 부호 있는 32비트 정수 값을 담아요.
하지만 int32 타입과 달리, rune 타입에 저장된 정수 값은 하나의 유니코드 문자를 나타내요.
유니코드는 ASCII의 상위 집합으로, 모든 문자에 고유한 번호를 부여해서 문자를 나타내요. 이 고유한 번호를 유니코드 코드 포인트라고 해요. 유니코드는 다양한 알파벳, 숫자, 기호, 심지어 이모지까지 세상의 모든 문자를 유니코드 코드 포인트로 나타내는 것을 목표로 해요.
Go에서 rune 타입은 하나의 유니코드 코드 포인트를 나타내요.
다음 표는 몇 가지 유니코드 문자를 그 유니코드 코드 포인트, 십진수 값과 함께 보여줘요:
| Unicode Character | Unicode Code Point | Decimal Value |
|---|---|---|
| 0 | U+0030 |
48 |
| A | U+0041 |
65 |
| a | U+0061 |
97 |
| ¿ | U+00BF |
191 |
| π | U+03C0 |
960 |
| 🧠 | U+1F9E0 |
129504 |
UTF-8은 모든 유니코드 코드 포인트를 1, 2, 3, 4바이트로 인코딩하는 데 사용되는 가변 길이 문자 인코딩이에요.
유니코드 코드 포인트는 최대 4바이트로 인코딩될 수 있기 때문에, rune 타입은 최대 4바이트의 데이터를 담을 수 있어야 해요.
그래서 rune 타입이 int32의 별칭인 거예요. int32 타입은 최대 4바이트의 데이터를 담을 수 있으니까요.
Go 소스 코드 파일은 UTF-8로 인코딩돼요.
rune 타입의 변수는 문자를 작은따옴표 안에 넣어 선언해요:
myRune := '¿'
rune은 int32의 별칭일 뿐이라서, rune의 타입을 출력하면 int32가 나와요:
myRune := '¿'
fmt.Printf("myRune type: %T\n", myRune)
// Output: myRune type: int32
마찬가지로 rune의 값을 출력하면 정수(십진수) 값이 나와요:
myRune := '¿'
fmt.Printf("myRune value: %v\n", myRune)
// Output: myRune value: 191
rune이 나타내는 유니코드 문자를 출력하려면 %c 서식 동사를 사용해요:
myRune := '¿'
fmt.Printf("myRune Unicode character: %c\n", myRune)
// Output: myRune Unicode character: ¿
rune이 나타내는 유니코드 코드 포인트를 출력하려면 %U 서식 동사를 사용해요:
myRune := '¿'
fmt.Printf("myRune Unicode code point: %U\n", myRune)
// Output: myRune Unicode code point: U+00BF
Go의 문자열은 UTF-8로 인코딩되기 때문에 유니코드 문자를 담고 있어요. 문자열의 문자는 나타내는 유니코드 문자에 따라 1, 2, 3, 4바이트로 저장되고 인코딩돼요.
Go에서는 슬라이스로 시퀀스를 나타내고, 이 슬라이스는 range로 순회할 수 있어요.
문자열을 순회할 때 Go는 문자열을 일련의 rune으로 변환하는데, 각 rune은 4바이트예요 (기억하세요, rune 타입은 int32의 별칭이에요!).
문자열이 그저 바이트의 슬라이스일지라도, range 키워드는 문자열의 바이트가 아니라 rune을 순회해요.
이 예제에서 index 변수는 현재 rune의 바이트 시퀀스가 시작하는 인덱스를 나타내고, char 변수는 현재 rune을 나타내요:
myString := "❗hello"
for index, char := range myString {
fmt.Printf("Index: %d\tCharacter: %c\t\tCode Point: %U\n", index, char, char)
}
// Output:
// Index: 0 Character: ❗ Code Point: U+2757
// Index: 3 Character: h Code Point: U+0068
// Index: 4 Character: e Code Point: U+0065
// Index: 5 Character: l Code Point: U+006C
// Index: 6 Character: l Code Point: U+006C
// Index: 7 Character: o Code Point: U+006F
rune은 1, 2, 3, 4바이트로 저장될 수 있기 때문에, 문자열의 길이가 항상 문자열의 문자 수와 같지는 않아요.
문자열의 길이를 바이트 단위로 구하려면 내장 len 함수를, 문자열의 rune 개수를 구하려면 utf8.RuneCountInString 함수를 사용해요:
import "unicode/utf8"
myString := "❗hello"
stringLength := len(myString)
numberOfRunes := utf8.RuneCountInString(myString)
fmt.Printf("myString - Length: %d - Runes: %d\n", stringLength, numberOfRunes)
// Output: myString - Length: 8 - Runes: 6
조직의 로그 관리를 돕는 로그 라이브러리를 만들라는 임무를 받았어요. 이 라이브러리를 사용하면 어떤 애플리케이션이 주어진 로그를 남겼는지 알아내고, 손상된 로그를 고치고, 주어진 로그 줄이 특정 글자 수 제한 안에 들어오는지 판단할 수 있어요.
로그는 저마다 고유한 로그 형식을 사용하는 여러 애플리케이션에서 만들어져요. 로그를 로그 집계 시스템에 저장하려면, 그 로그를 남긴 애플리케이션을 먼저 알아내야 해요.
로그 줄을 받아 그 로그를 남긴 애플리케이션을 반환하는 Application 함수를 구현해요.
주어진 로그 줄을 남긴 애플리케이션을 알아내려면, 다음 표에 정리된 특정 문자를 로그 줄에서 찾아요:
| 애플리케이션 | 문자 | 유니코드 코드 포인트 |
|---|---|---|
recommendation |
❗ | U+2757 |
search |
🔍 | U+1F50D |
weather |
☀ | U+2600 |
로그 줄에 위 표에 있는 문자 중 어느 것도 없다면, 호출한 쪽에 default를 반환해요.
로그 줄에 위 표에 있는 문자가 둘 이상 있다면, 로그 줄을 왼쪽에서 오른쪽으로 훑으면서 처음 찾은 문자에 해당하는 애플리케이션을 반환해요.
Application("❗ recommended search product 🔍")
// => recommendation
로깅 인프라에 드물지만 끈질기게 남아 있는 버그 때문에, 로그의 특정 문자가 손상될 수 있어요. 손상된 문자와 그 원래 값을 알아내는 데 시간을 들인 뒤, 손상된 로그를 고치는 기능을 로그 라이브러리에 추가하기로 했어요.
로그 줄, 손상된 문자, 원래 값을 받아, 손상된 문자가 나오는 곳을 모두 원래 값으로 바꾼 로그 줄을 반환하는 Replace 함수를 구현해요.
log := "please replace '👎' with '👍'"
Replace(log, '👎', '👍')
// => please replace '👍' with '👍'"
로그를 표시하는 시스템에는 로그 줄 하나에 표시할 수 있는 글자 수 제한이 있어요. 그래서 사용자들이 이 라이브러리에, 로그 줄이 특정 글자 수 제한 안에 들어오는지 판단하는 도우미 함수를 넣어 달라고 요청하고 있어요.
로그 줄과 글자 수 제한을 받아, 그 로그 줄이 제한 안에 들어오는지 여부를 반환하는 WithinLimit 함수를 구현해요.
WithinLimit("hello❗", 6)
// => true
Exercism에 가입하고 Go 트랙을 개념 34개연습 문제 165개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.