C++은 강력한 프로그래밍 언어지만 그에 반해 부족한 부분도 많다. 특히 문자열 관련해서는 타의 추종을 불허할 정도로 불편하다. 이번 포스트에서는 C++에서 UTF-8 문자열의 개수를 세어 보도록 하겠다.
UTF-8 인코딩 규칙
보통 UTF-8관련 포스팅이라면, UTF-8의 인코딩 역사 부터, 유니코드와의 관계 등등 장황한 이야기가 펼쳐지지만 여기서는 코드를 작성 할 수 있을 정도의 짧고 얇은 지식만 요약하고 넘어 간다. 밑줄 그어진 부분을 집중해서 보자.
UTF-8인코딩은,
유니코드 한 문자를 나타내기 위해
1바이트에서 부터 4바이트까지 사용하는
"가변 길이" 문자열 인코딩 방식이다.
- 문자에 따라 문자를 표현하기 위해 1바이트에서 4바이트까지 다양한 길이를 가진다.
- 1바이트로 표시된 문자의 최상위 비트는 항상 0이다
- 2바이트 이상으로 표시된 문자의 경우, 첫바이트의 상위 비트들이 그 문자를 표시하는데 필요한 바이트 수를 결정한다.
2바이트는 110으로 시작
3바이트는 1110으로 시작
4바이트는 11110으로 시작 - 첫 바이트가 아닌 나머지 바이트들은 상위 2비트가 항상 10이다.
바이트수 | UTF8 표현(이진법) | 설명 |
1바이트 문자 | 0xxxxxxx | 최상위 비트가 0이다 |
2바이트 문자 | 110xxxxx 10xxxxxx |
첫바이트는 110으로 시작 나머지 바이트들은 10으로 시작 |
3바이트 문자 | 1110xxxx 10xxxxxx 10xxxxxx |
첫바이트는 1110으로 시작 나머지 바이트들은 10으로 시작 |
4바이트 문자 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
첫바이트는 11110으로 시작 나머지 바이트들은 10으로 시작 |
Example
위 규칙을 기반으로 바이트스트림(여기서는 std::string)에 들어 있는 UTF-8문자열의 글자 개수를 카운팅하는 예제를 만들어 보았다. 예제 파일과 예제에서 사용된 utf8.txt는 본 포스트의 부록 2항목에서 다운받을 수 있다.
코드 자체는 어렵지 않으므로 코드 설명은 주석으로 대체한다.
#include <iostream>
#include <fstream>
#include <string>
#ifdef _WIN32
#include <Windows.h>
#endif
size_t UTF8Length(const std::string& str)
{
size_t utf8_char_count = 0;
for (int i = 0; i < str.length();)
{
// 4바이트 문자인지 확인
// 0xF0 = 1111 0000
if (0xF0 == (0xF0 & str[i]))
{
// 나머지 3바이트 확인
// 0x80 = 1000 0000
if (0x80 != (0x80 & str[i + 1]) || 0x80 != (0x80 & str[i + 2]) || 0x80 != (0x80 & str[i + 3]))
{
throw std::exception("not utf-8 encoded string");
}
i += 4;
utf8_char_count++;
continue;
}
// 3바이트 문자인지 확인
// 0xE0 = 1110 0000
else if (0xE0 == (0xE0 & str[i]))
{
// 나머지 2바이트 확인
// 0x80 = 1000 0000
if (0x80 != (0x80 & str[i + 1]) || 0x80 != (0x80 & str[i + 2]))
{
throw std::exception("not utf-8 encoded string");
}
i += 3;
utf8_char_count++;
continue;
}
// 2바이트 문자인지 확인
// 0xC0 = 1100 0000
else if (0xC0 == (0xC0 & str[i]))
{
// 나머지 1바이트 확인
// 0x80 = 1000 0000
if (0x80 != (0x80 & str[i + 1]))
{
throw std::exception("not utf-8 encoded string");
}
i += 2;
utf8_char_count++;
continue;
}
// 최상위 비트가 0인지 확인
else if(0 == (str[i] >> 7))
{
i += 1;
utf8_char_count++;
}
else
{
throw std::exception("not utf-8 encoded string");
}
}
return utf8_char_count;
}
int main()
{
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
const std::string file_path = "utf8.txt";
std::ifstream is(file_path);
if (true == is.is_open())
{
std::string line;
while (std::getline(is, line))
{
try {
std::cout << line << ", count:" << UTF8Length(line) << ", byte length:" << line.length() << std::endl;
}
catch(const std::exception& e)
{
std::cout << e.what() << std::endl;
}
}
is.close();
}
}
Output
마치며
이해가 필요한 것이 아닌, 규칙대로 따라 만들기만 하면 되는 내용이라 설명이 부족하다고 느낄 수 있다. 추가 문의 사항이 있다면 아래 댓글로 남겨 주길 바란다. 최대한 빨리 답변 할 수 있도록 하겠다.
부록 1. 같이 보면 좋은 글
- [C++] 윈도우 콘솔에서 UTF-8 출력하기
- 문자 인코딩에 대한 이해
- Getting the actual length of a UTF-8 encoded std::string?
- How to count characters in a unicode string in C
- C++ UTF-8 strlen function
- Unicode in C and C++
- Unicode와 UTF-8 간단히 이해하기
부록 2. 첨부 파일