검색결과 리스트
글
C++ / Base64 Encoder
(UTF-8 base64 encoding )
Intro
C++ 에서 base64 로 encoding 을 하는것은 어렵지 않습니다.
왜냐면 base64 encoding 자체가 간단하기 때문입니다.
아시다시피 base64 인코딩은, 문자열(혹은 메모리값)을 8bit 가 아닌 6bit 단위로 끊고, 이를 character map 에 해당하는 값으로 치환하면 됩니다.
오히려 이런 bit 단위 처리에서는 java, .NET 보다는 C/C++ 이 더 편합니다.
그런데 C++ 에서는 문제가 되는것이 있었으니, 바로 character set 입니다.
EUC-KR, UTF-8, UTF-16 같은 것들이죠.
그리고 멀티바이트니 유니코드니, 복잡합니다.
이런 이유로 java, .NET, Web(javascript)에서 encoding 한 값과 C++ 에서 인코딩한 값이 다르기 일쑤입니다.
Intent
java, .NET, Web 처럼 C++ 에서도 한글/일본어 같은 3byte 문자도 base64 인코딩을 정상적으로(?) 할 수 있도록 해 봅시다.
먼저 다음은 Java 에서 "대한민국" 이라는 문자열을 base64로 인코딩한 코드입니다.
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.binary.Base64;
public class EncodeTest {
public static void main(String args[]) throws UnsupportedEncodingException {
String plain = "대한민국";
System.out.println(Base64.encodeBase64URLSafeString(plain.getBytes("utf-8")));
}
}
결과는 다음과 같네요.
이번에는 웹에서 해 보도록 하죠.
http://www.opinionatedgeek.com/dotnet/tools/base64encode/
결과는 아래와 같습니다.
차이점이 보이시나요?
'-' 와 '+' 입니다.
이 부분은 base64 encoding 의 옵션에 해당하는 부분인데, 이부분에 대해서는 wikipedia.org 를 참고하시기 바랍니다.
('=' 에 대한 부분도 읽어보세요)
아무튼 결과는 동일하게 나왔습니다.
이젠 C++ 에서 해 보도록 하죠.
//===========================================================
// base64
//===========================================================
std::wstring base64Encode(const wstring input){
//string utf8_input = wstrToUtf8(input);
unsigned char const* buffer = (unsigned const char*)(input.c_str());
size_t size = input.length();
using std::wstring;
static wchar_t const* base64Table =
L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// for =
// size_t base64Size = (size + 2 - ((size + 2) % 3)) / 3 * 4;
// wstring result(base64Size, L'=');
size_t base64Size = ceil(size * 4.0 / 3);
wstring result(base64Size, L'');
unsigned char const* s = buffer; // source pointer
size_t di = 0; // destination index
for(size_t i = 0; i < size / 3; i++){
// input: 0000_0000 0000_0000 0000_0000
//
// out1: 0000_00
// out2: 00 0000
// out3: _0000 00
// out4: 00_0000
result[di++] = base64Table[s[0] >> 2];
result[di++] = base64Table[((s[0] << 4) | (s[1] >> 4)) & 0x3f];
result[di++] = base64Table[((s[1] << 2) | (s[2] >> 6)) & 0x3f];
result[di++] = base64Table[s[2] & 0x3f];
s += 3;
}
size_t remainSize = size % 3;
switch(remainSize){
case 0:
break;
case 1:
result[di++] = base64Table[s[0] >> 2];
result[di++] = base64Table[(s[0] << 4) & 0x3f];
break;
case 2:
result[di++] = base64Table[s[0] >> 2];
result[di++] = base64Table[((s[0] << 4) | (s[1] >> 4)) & 0x3f];
result[di++] = base64Table[(s[1] << 2) & 0x3f];
break;
default:
throw std::logic_error("Should not happen.");
}
return result;
}
int main() {
wcout.imbue(locale("korean"));
wstring plain(L"대한민국");
wcout << base64Encode(plain).c_str() << endl;
return 0;
}
결과는 아래와 같습니다.
C++ 에서는 Unicode 관련 문제로 인하여 결과값이 다르게 나옵니다.
참고로 위 코드는 '문자집합' 을 '유니코드 문자 집합 사용' 으로 한 프로젝트입니다.
Use Case
원인은 유니코드니, 멀티바이트니, UTF-8 이니 하는 것들이 문제입니다.
실제로 Visual Studio 에서 wstring::c_str() 을 통해 가져온 문자열의 메모리 값을 살펴보면,
한글이 2byte 로 처리되어 있는 것을 볼 수 있습니다.
결국 base64 로 인코딩 하기 전에 wstring 의 값을 utf-8 로 변환해 주어야 합니다.
아래는 정상적으로 동작하는 C++ 코드입니다.
차이점은 코드로 확인하시기 바랍니다.
void wstrToUtf8(string& dest, const wstring& src){
dest.clear();
for (size_t i = 0; i < src.size(); i++){
wchar_t w = src[i];
if (w <= 0x7f)
dest.push_back((char)w);
else if (w <= 0x7ff){
dest.push_back(0xc0 | ((w >> 6)& 0x1f));
dest.push_back(0x80| (w & 0x3f));
}
else if (w <= 0xffff){
dest.push_back(0xe0 | ((w >> 12)& 0x0f));
dest.push_back(0x80| ((w >> 6) & 0x3f));
dest.push_back(0x80| (w & 0x3f));
}
else if (w <= 0x10ffff){
dest.push_back(0xf0 | ((w >> 18)& 0x07));
dest.push_back(0x80| ((w >> 12) & 0x3f));
dest.push_back(0x80| ((w >> 6) & 0x3f));
dest.push_back(0x80| (w & 0x3f));
}
else
dest.push_back('?');
}
}
string wstrToUtf8(const wstring& str){
string result;
wstrToUtf8(result, str);
return result;
}
//===========================================================
// base64
//===========================================================
std::wstring base64Encode(const wstring input){
string utf8_input = wstrToUtf8(input);
unsigned char const* buffer = (unsigned const char*)(utf8_input.c_str());
size_t size = utf8_input.length();
using std::wstring;
static wchar_t const* base64Table =
L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// for =
// size_t base64Size = (size + 2 - ((size + 2) % 3)) / 3 * 4;
// wstring result(base64Size, L'=');
size_t base64Size = ceil(size * 4.0 / 3);
wstring result(base64Size, L'');
unsigned char const* s = buffer; // source pointer
size_t di = 0; // destination index
for(size_t i = 0; i < size / 3; i++){
// input: 0000_0000 0000_0000 0000_0000
//
// out1: 0000_00
// out2: 00 0000
// out3: _0000 00
// out4: 00_0000
result[di++] = base64Table[s[0] >> 2];
result[di++] = base64Table[((s[0] << 4) | (s[1] >> 4)) & 0x3f];
result[di++] = base64Table[((s[1] << 2) | (s[2] >> 6)) & 0x3f];
result[di++] = base64Table[s[2] & 0x3f];
s += 3;
}
size_t remainSize = size % 3;
switch(remainSize){
case 0:
break;
case 1:
result[di++] = base64Table[s[0] >> 2];
result[di++] = base64Table[(s[0] << 4) & 0x3f];
break;
case 2:
result[di++] = base64Table[s[0] >> 2];
result[di++] = base64Table[((s[0] << 4) | (s[1] >> 4)) & 0x3f];
result[di++] = base64Table[(s[1] << 2) & 0x3f];
break;
default:
throw std::logic_error("Should not happen.");
}
return result;
}
int main() {
wcout.imbue(locale("korean"));
wstring plain(L"대한민국");
wcout << base64Encode(plain).c_str() << endl;
return 0;
}
결과는..
Result
문자열 자릿수에 따라 = 로 buff 가 채워지는 방식이 있고, 위 코드처럼 별도로 채우지 않는 방식이 있습니다.
또한 +를 - 로 치환한다거나(web에서 사용하기 위해서), 일정 크기길이가 되면 CR/LF 를 먹이는 방식도 있죠.
이런 각종 옵션들은 base64 문서를 참고하시기 바랍니다.
규칙을 이해하고 나시면, 코드에 적용하는건 쉽게 하실거라고 생각합니다.
마지막으로 Decoding 은 위 코드를 참고하여, 만들어 보시기 바랍니다. :)
'Microsoft > Tips' 카테고리의 다른 글
Windows 에서 파일을 연결프로그램으로 실행하는 방법 (0) | 2011.09.20 |
---|---|
[팁] 윈도우즈 서비스에서 시스템 종료하는 방법 (0) | 2011.05.31 |
리눅스 제거하기 (1) | 2011.02.04 |
RECENT COMMENT