本文打算用C++程序跟踪UTF-8字符的二进制格式。从实践上感受一下UTF-8的应用。
开发环境是UBuntu12.04 32bit OS. GCC 4.6.3,系统字节顺序是little endian.
如果有汉字‘一’,首先通过一个网站工具:http://rishida.net/tools/conversion/ 可以查到它的Unicode码是:0x4E00
用UTF-8对0x4E00进行编码后是:E4 B8 80,三字节。
下面的代码用来打印二进制码:
#include "test.h"
#include "util/endian.h"
#include "util/utf.h"
#include
using namespace std;
int main(int argc, char ** argv) {
// TEST(3 > 2);
char const * p = "一";
cout << PrintStringAsBinaryString(p) << endl;
string str = "一";
cout << PrintStringAsBinaryString(str) << endl;
cout << IsLittleEndian() << endl;
}
#ifndef UTIL_UTF_H_
#define UTIL_UTF_H_
#include "util/endian.h"
string PrintStringAsBinaryString(char const* p) {
stringstream stream;
for (size_t i = 0; i < strlen(p); ++i) {
stream << PrintIntAsBinaryString(p[i]);
stream << " ";
}
return stream.str();
}
string PrintStringAsBinaryString(string const& str) {
stringstream stream;
for (size_t i = 0; i < str.size(); ++i) {
stream << PrintIntAsBinaryString(str[i]);
stream << " ";
}
return stream.str();
}
#endif
// T must be one of integer type
template
string PrintIntAsBinaryString(T v) {
stringstream stream;
int i = sizeof(T) * 8 - 1;
while (i >= 0) {
stream << Bit_Value(v, i);
--i;
}
return stream.str();
}
// Get the bit value specified by the index
// index starts with 0
template
int Bit_Value(T value, uint8_t index) {
return (value & (1 << index)) == 0 ? 0 : 1;
}
显示结果是:
11100100 10111000 10000000
刚好是E4 B8 80
这里可以看到Leading byte就是最高位的字节E4, 就存放在char const * p所指的的内存的起始地址,因此可以看出是Big endian,也就是在系统中实测默认应该采用的是UTF-8 BE编码。
下面继续奋战,把UTF-8转换成Unicode码,也就是code point。注意code_point类型的定义
typedef uint32_t code_point
现在测试代码修改一下,引入boost::locale库,这个算法自己也可以写,不过时间紧张,先用成熟的库吧。
#include "test.h"
#include "util/endian.h"
#include "util/utf.h"
#include
#include
using namespace std;
using namespace boost::locale::utf;
int main(int argc, char ** argv) {
// TEST(3 > 2);
char const * p = "一";
cout << PrintStringAsBinaryString(p) << endl;
string str = "一";
cout << PrintStringAsBinaryString(str) << endl;
code_point c = utf_traits::decode(p, p + 3);
cout << "code point: 0x" << std::hex << c << " binary format:B" << PrintIntAsBinaryString(c) << endl;
}
倒数第二行代码就是调用了decode进行解码。
结果是:
code point: 0x4e00 binary format:B00000000000000000100111000000000
非常理想。也可以传递string::iterator作为参数,只是注意string::begin()不能直接作为参数使用,而要像这样:
string::iterator itor = str.begin();
utf_traits::decode(itor, str.end());
因为decode的第一个参数是引用,decode内部会执行++操作。而string::begin()是不允许改变的,因此编译会报错。