UTF-8、UTF-16、UTF-32编码的相互转换(不使用现成的函数)
最近在考虑写一个可以跨平台的通用字符串类,首先需要搞定的就是编码转换问题。
vs默认保存代码文件,使用的是本地code(中文即GBK,日文即Shift-JIS),也可以使用带BOM的UTF-8。
gcc则是UTF-8,有无BOM均可(源代码的字符集可以由参数-finput-charset指定)。
那么源代码可以采用带BOM的UTF-8来保存。而windows下的unicode是UTF-16编码;Linux则使用UTF-8或UTF-32。因此不论在哪种系统里,程序在处理字符串时都需要考虑UTF编码之间的相互转换。
下面直接贴出算法代码。算法上我借鉴了秦建辉(http://blog.csdn.net/jhqin)的UnicodeConverter,只是在外面增加了一些泛型处理,让使用相对简单。
核心算法(来自UnicodeConverter):
- namespace transform
- {
- /*
- UTF-32 to UTF-8
- */
- inline static size_t utf(uint32 src, uint8* des)
- {
- if (src == 0) return 0;
- static const byte PREFIX[] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
- static const uint32 CODE_UP[] =
- {
- 0x80, // U+00000000 - U+0000007F
- 0x800, // U+00000080 - U+000007FF
- 0x10000, // U+00000800 - U+0000FFFF
- 0x200000, // U+00010000 - U+001FFFFF
- 0x4000000, // U+00200000 - U+03FFFFFF
- 0x80000000 // U+04000000 - U+7FFFFFFF
- };
- size_t i, len = sizeof(CODE_UP) / sizeof(uint32);
- for(i = 0; i < len; ++i)
- if (src < CODE_UP[i]) break;
- if (i == len) return 0; // the src is invalid
- len = i + 1;
- if (des)
- {
- for(; i > 0; --i)
- {
- des[i] = static_cast<uint8>((src & 0x3F) | 0x80);
- src >>= 6;
- }
- des[0] = static_cast<uint8>(src | PREFIX[len - 1]);
- }
- return len;
- }
- /*
- UTF-8 to UTF-32
- */
- inline static size_t utf(const uint8* src, uint32& des)
- {
- if (!src || (*src) == 0) return 0;
- uint8 b = *(src++);
- if (b < 0x80)
- {
- des = b;
- return 1;
- }
- if (b < 0xC0 || b > 0xFD) return 0; // the src is invalid
- size_t len;
- if (b < 0xE0)
- {
- des = b & 0x1F;
- len = 2;
- }
- else
- if (b < 0xF0)
- {
- des = b & 0x0F;
- len = 3;
- }
- else
- if (b < 0xF8)
- {
- des = b & 0x07;
- len = 4;
- }
- else
- if (b < 0xFC)
- {
- des = b & 0x03;
- len = 5;
- }
- else
- {
- des = b & 0x01;
- len = 6;
- }
- size_t i = 1;
- for (; i < len; ++i)
- {
- b = *(src++);
- if (b < 0x80 || b > 0xBF) return 0; // the src is invalid
- des = (des << 6) + (b & 0x3F);
- }
- return len;
- }
- /*
- UTF-32 to UTF-16
- */
- inline static size_t utf(uint32 src, uint16* des)
- {
- if (src == 0) return 0;
- if (src <= 0xFFFF)
- {
- if (des) (*des) = static_cast<uint16>(src);
- return 1;
- }
- else
- if (src <= 0xEFFFF)
- {
- if (des)
- {
- des[0] = static_cast<uint16>(0xD800 + (src >> 10) - 0x40); // high
- des[1] = static_cast<uint16>(0xDC00 + (src & 0x03FF)); // low
- }
- return 2;
- }
- return 0;
- }
- /*
- UTF-16 to UTF-32
- */
- inline static size_t utf(const uint16* src, uint32& des)
- {
- if (!src || (*src) == 0) return 0;
- uint16 w1 = src[0];
- if (w1 >= 0xD800 && w1 <= 0xDFFF)
- {
- if (w1 < 0xDC00)
- {
- uint16 w2 = src[1];
- if (w2 >= 0xDC00 && w2 <= 0xDFFF)
- {
- des = (w2 & 0x03FF) + (((w1 & 0x03FF) + 0x40) << 10);
- return 2;
- }
- }
- return 0; // the src is invalid
- }
- else
- {
- des = w1;
- return 1;
- }
- }
- }
上面这些算法都是针对单个字符的,并且是UTF-32和UTF-16/8之间的互转。
通过上面的算法,可以得到UTF-16和UTF-8之间的单字符转换算法:
- namespace transform
- {
- /*
- UTF-16 to UTF-8
- */
- inline static size_t utf(uint16 src, uint8* des)
- {
- // make utf-16 to utf-32
- uint32 tmp;
- if (utf(&src, tmp) != 1) return 0;
- // make utf-32 to utf-8
- return utf(tmp, des);
- }
- /*
- UTF-8 to UTF-16
- */
- inline static size_t utf(const uint8* src, uint16& des)
- {
- // make utf-8 to utf-32
- uint32 tmp;
- size_t len = utf(src, tmp);
- if (len == 0) return 0;
- // make utf-32 to utf-16
- if (utf(tmp, &des) != 1) return 0;
- return len;
- }
- }
同样,通过上面的单字符转换算法,可以得到整个字符串的转换算法:
- namespace transform
- {
- /*
- UTF-X: string to string
- */
- template <typename T>
- size_t utf(const uint32* src, T* des) // UTF-32 to UTF-X(8/16)
- {
- if (!src || (*src) == 0) return 0;
- size_t num = 0;
- for(; *src; ++src)
- {
- size_t len = utf(*src, des);
- if (len == 0) break;
- if (des) des += len;
- num += len;
- }
- if (des) (*des) = 0;
- return num;
- }
- template <typename T>
- size_t utf(const T* src, uint32* des) // UTF-X(8/16) to UTF-32
- {
- if (!src || (*src) == 0) return 0;
- size_t num = 0;
- while(*src)
- {
- uint32 tmp;
- size_t len = utf(src, tmp);
- if (len == 0) break;
- if (des)
- {
- (*des) = tmp;
- ++des;
- }
- src += len;
- num += 1;
- }
- if (des) (*des) = 0;
- return num;
- }
- template <typename T, typename U>
- size_t utf(const T* src, U* des) // UTF-X(8/16) to UTF-Y(16/8)
- {
- if (!src || (*src) == 0) return 0;
- size_t num = 0;
- while(*src)
- {
- // make utf-x to ucs4
- uint32 tmp;
- size_t len = utf(src, tmp);
- if (len == 0) break;
- src += len;
- // make ucs4 to utf-y
- len = utf(tmp, des);
- if (len == 0) break;
- if (des) des += len;
- num += len;
- }
- if (des) (*des) = 0;
- return num;
- }
- }
有了这些之后,我们已经可以完整的做UTF-8/16/32之间的相互转换了,但是这些函数的使用仍然不是很方便。
比如我现在想把一个UTF-8字符串转换成一个wchar_t*字符串,我得这样写:
- const uint8* c = (uint8*)"こんにちわ、世界";
- size_t n = (sizeof(wchar_t) == 2) ?
- transform::utf(c, (uint16*)0) :
- transform::utf(c, (uint32*)0);
- wchar_t* s = new wchar_t[n];
- if (sizeof(wchar_t) == 2)
- transform::utf(c, (uint16*)s);
- else
- transform::utf(c, (uint32*)s);
这显然是一件很抽搐的事情,因为wchar_t在不同的操作系统(windows/linux)里有不同的sizeof长度。
上面的类型强制转换只是为了去适配合适的函数重载,当然我们也可以通过函数名来区分这些函数:比如分别叫utf8_to_utf32之类的。但是这改变不了写if-else来适配长度的问题。
显然这里可以通过泛型来让算法更好用。
首先,需要被抽离出来的就是参数的类型大小和类型本身的依赖关系:
- template <size_t X> struct utf_type;
- template <> struct utf_type<1> { typedef uint8 type_t; };
- template <> struct utf_type<2> { typedef uint16 type_t; };
- template <> struct utf_type<4> { typedef uint32 type_t; };
然后,实现一个简单的check算法,这样后面就可以利用SFINAE的技巧筛选出合适的算法函数:
- template <size_t X, typename T>
- struct check
- {
- static const bool value =
- ((sizeof(T) == sizeof(typename utf_type<X>::type_t)) && !is_pointer<T>::value);
- };
下面我们需要一个detail,即泛型适配的细节。从上面的算法函数参数中,我们可以很容易的观察出一些规律:
只要是由大向小转换(比如32->16,或16->8)的,其对外接口可以抽象成这两种形式:
- type_t utf(T src, U* des)
- type_t utf(const T* src, U* des)
而由小向大的转换,则是下面这两种形式:
- type_t utf(const T* src, U& des)
- type_t utf(const T* src, U* des)
再加上第二个指针参数是可以给一个默认值(空指针)的,因此适配的泛型类就可以写成这样:
- template <size_t X, size_t Y, bool = (X > Y), bool = (X != Y)>
- struct detail;
- /*
- UTF-X(32/16) to UTF-Y(16/8)
- */
- template <size_t X, size_t Y>
- struct detail<X, Y, true, true>
- {
- typedef typename utf_type<X>::type_t src_t;
- typedef typename utf_type<Y>::type_t des_t;
- template <typename T, typename U>
- static typename enable_if<check<X, T>::value && check<Y, U>::value,
- size_t>::type_t utf(T src, U* des)
- {
- return transform::utf((src_t)(src), (des_t*)(des));
- }
- template <typename T>
- static typename enable_if<check<X, T>::value,
- size_t>::type_t utf(T src)
- {
- return transform::utf((src_t)(src), (des_t*)(0));
- }
- template <typename T, typename U>
- static typename enable_if<check<X, T>::value && check<Y, U>::value,
- size_t>::type_t utf(const T* src, U* des)
- {
- return transform::utf((const src_t*)(src), (des_t*)(des));
- }
- template <typename T>
- static typename enable_if<check<X, T>::value,
- size_t>::type_t utf(const T* src)
- {
- return transform::utf((src_t)(src), (des_t*)(0));
- }
- };
- /*
- UTF-X(16/8) to UTF-Y(32/16)
- */
- template <size_t X, size_t Y>
- struct detail<X, Y, false, true>
- {
- typedef typename utf_type<X>::type_t src_t;
- typedef typename utf_type<Y>::type_t des_t;
- template <typename T, typename U>
- static typename enable_if<check<X, T>::value && check<Y, U>::value,
- size_t>::type_t utf(const T* src, U& des)
- {
- des_t tmp; // for disable the warning strict-aliasing from gcc 4.4
- size_t ret = transform::utf((const src_t*)(src), tmp);
- des = tmp;
- return ret;
- }
- template <typename T, typename U>
- static typename enable_if<check<X, T>::value && check<Y, U>::value,
- size_t>::type_t utf(const T* src, U* des)
- {
- return transform::utf((const src_t*)(src), (des_t*)(des));
- }
- template <typename T>
- static typename enable_if<check<X, T>::value,
- size_t>::type_t utf(const T* src)
- {
- return transform::utf((const src_t*)(src), (des_t*)(0));
- }
- };
最后的外敷类收尾就可以相当的简单:
- template <typename T, typename U>
- struct converter
- : detail<sizeof(T), sizeof(U)>
- {};
通过上面的detail,我们也可以很轻松的写出一个通过指定8、16这些数字,来控制选择哪些转换算法的外敷模板。
有了converter,同类型的需求(指UTF-8转wchar_t)就可以变得轻松愉快很多:
- const char* c = "こんにちわ、世界";
- wstring s;
- size_t n; wchar_t w;
- while (!!(n = converter<char, wchar_t>::utf(c, w))) // 这里的!!是为了屏蔽gcc的警告
- {
- s.push_back(w);
- c += n;
- }
- FILE* fp = fopen("test_converter.txt", "wb");
- fwrite(s.c_str(), sizeof(wchar_t), s.length(), fp);
- fclose(fp);
上面这一小段代码是将一段UTF-8的文字逐字符转换为wchar_t,并一个个push_back到wstring里,最后把转换完毕的字符串输出到test_converter.txt里。
其实上面的泛型还是显得累赘了。为什么不直接在transform::utf上使用泛型参数呢?
一开始只想到上面那个方法,自然是由于惯性的想要手动指定如何转换编码的缘故,比如最开始的想法,是想做成类似这样的模板:utf<8, 32>(s1, s2),指定两个数字,来决定输入和输出的格式。
后来发现,直接指定字符串/字符的类型或许更加直接些。
现在回头再看看,其实转换所需要的字长(8、16、32)已经在参数的类型中指定了:8bits的char或byte类型肯定不会是用来存放UTF-32的嘛。。
所以只需要把上面核心算法的参数泛型化就可以了。这时代码就会写成下面这个样子:
- namespace transform
- {
- namespace private_
- {
- template <size_t X> struct utf_type;
- template <> struct utf_type<1> { typedef uint8 type_t; };
- template <> struct utf_type<2> { typedef uint16 type_t; };
- template <> struct utf_type<4> { typedef uint32 type_t; };
- template <typename T, size_t X>
- struct check
- {
- static const bool value =
- ((sizeof(T) == sizeof(typename utf_type<X>::type_t)) && !is_pointer<T>::value);
- }
- }
- using namespace transform::private_;
- /*
- UTF-32 to UTF-8
- */
- template <typename T, typename U>
- typename enable_if<check<T, 4>::value && check<U, 1>::value,
- size_t>::type_t utf(T src, U* des)
- {
- if (src == 0) return 0;
- static const byte PREFIX[] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
- static const uint32 CODE_UP[] =
- {
- 0x80, // U+00000000 - U+0000007F
- 0x800, // U+00000080 - U+000007FF
- 0x10000, // U+00000800 - U+0000FFFF
- 0x200000, // U+00010000 - U+001FFFFF
- 0x4000000, // U+00200000 - U+03FFFFFF
- 0x80000000 // U+04000000 - U+7FFFFFFF
- };
- size_t i, len = sizeof(CODE_UP) / sizeof(uint32);
- for(i = 0; i < len; ++i)
- if (src < CODE_UP[i]) break;
- if (i == len) return 0; // the src is invalid
- len = i + 1;
- if (des)
- {
- for(; i > 0; --i)
- {
- des[i] = static_cast<U>((src & 0x3F) | 0x80);
- src >>= 6;
- }
- des[0] = static_cast<U>(src | PREFIX[len - 1]);
- }
- return len;
- }
- /*
- UTF-8 to UTF-32
- */
- template <typename T, typename U>
- typename enable_if<check<T, 1>::value && check<U, 4>::value,
- size_t>::type_t utf(const T* src, U& des)
- {
- if (!src || (*src) == 0) return 0;
- uint8 b = *(src++);
- if (b < 0x80)
- {
- des = b;
- return 1;
- }
- if (b < 0xC0 || b > 0xFD) return 0; // the src is invalid
- size_t len;
- if (b < 0xE0)
- {
- des = b & 0x1F;
- len = 2;
- }
- else
- if (b < 0xF0)
- {
- des = b & 0x0F;
- len = 3;
- }
- else
- if (b < 0xF8)
- {
- des = b & 0x07;
- len = 4;
- }
- else
- if (b < 0xFC)
- {
- des = b & 0x03;
- len = 5;
- }
- else
- {
- des = b & 0x01;
- len = 6;
- }
- size_t i = 1;
- for (; i < len; ++i)
- {
- b = *(src++);
- if (b < 0x80 || b > 0xBF) return 0; // the src is invalid
- des = (des << 6) + (b & 0x3F);
- }
- return len;
- }
- /*
- UTF-32 to UTF-16
- */
- template <typename T, typename U>
- typename enable_if<check<T, 4>::value && check<U, 2>::value,
- size_t>::type_t utf(T src, U* des)
- {
- if (src == 0) return 0;
- if (src <= 0xFFFF)
- {
- if (des) (*des) = static_cast<U>(src);
- return 1;
- }
- else
- if (src <= 0xEFFFF)
- {
- if (des)
- {
- des[0] = static_cast<U>(0xD800 + (src >> 10) - 0x40); // high
- des[1] = static_cast<U>(0xDC00 + (src & 0x03FF)); // low
- }
- return 2;
- }
- return 0;
- }
- /*
- UTF-16 to UTF-32
- */
- template <typename T, typename U>
- typename enable_if<check<T, 2>::value && check<U, 4>::value,
- size_t>::type_t utf(const T* src, U& des)
- {
- if (!src || (*src) == 0) return 0;
- uint16 w1 = src[0];
- if (w1 >= 0xD800 && w1 <= 0xDFFF)
- {
- if (w1 < 0xDC00)
- {
- uint16 w2 = src[1];
- if (w2 >= 0xDC00 && w2 <= 0xDFFF)
- {
- des = (w2 & 0x03FF) + (((w1 & 0x03FF) + 0x40) << 10);
- return 2;
- }
- }
- return 0; // the src is invalid
- }
- else
- {
- des = w1;
- return 1;
- }
- }
- /*
- UTF-16 to UTF-8
- */
- template <typename T, typename U>
- typename enable_if<check<T, 2>::value && check<U, 1>::value,
- size_t>::type_t utf(T src, U* des)
- {
- // make utf-16 to utf-32
- uint32 tmp;
- if (utf(&src, tmp) != 1) return 0;
- // make utf-32 to utf-8
- return utf(tmp, des);
- }
- /*
- UTF-8 to UTF-16
- */
- template <typename T, typename U>
- typename enable_if<check<T, 1>::value && check<U, 2>::value,
- size_t>::type_t utf(const T* src, U& des)
- {
- // make utf-8 to utf-32
- uint32 tmp;
- size_t len = utf(src, tmp);
- if (len == 0) return 0;
- // make utf-32 to utf-16
- if (utf(tmp, &des) != 1) return 0;
- return len;
- }
- /*
- UTF-X: string to string
- */
- template <typename T, typename U>
- typename enable_if<check<T, 4>::value && (check<U, 1>::value || check<U, 2>::value),
- size_t>::type_t utf(const T* src, U* des) // UTF-32 to UTF-X(8/16)
- {
- if (!src || (*src) == 0) return 0;
- size_t num = 0;
- for(; *src; ++src)
- {
- size_t len = utf(*src, des);
- if (len == 0) break;
- if (des) des += len;
- num += len;
- }
- if (des) (*des) = 0;
- return num;
- }
- template <typename T, typename U>
- typename enable_if<(check<T, 1>::value || check<T, 2>::value) && check<U, 4>::value,
- size_t>::type_t utf(const T* src, U* des) // UTF-X(8/16) to UTF-32
- {
- if (!src || (*src) == 0) return 0;
- size_t num = 0;
- while(*src)
- {
- uint32 tmp;
- size_t len = utf(src, tmp);
- if (len == 0) break;
- if (des)
- {
- (*des) = tmp;
- ++des;
- }
- src += len;
- num += 1;
- }
- if (des) (*des) = 0;
- return num;
- }
- template <typename T, typename U>
- typename enable_if<(check<T, 1>::value && check<U, 2>::value) ||
- (check<T, 2>::value && check<U, 1>::value),
- size_t>::type_t utf(const T* src, U* des) // UTF-X(8/16) to UTF-Y(16/8)
- {
- if (!src || (*src) == 0) return 0;
- size_t num = 0;
- while(*src)
- {
- // make utf-x to utf-32
- uint32 tmp;
- size_t len = utf(src, tmp);
- if (len == 0) break;
- src += len;
- // make utf-32 to utf-y
- len = utf(tmp, des);
- if (len == 0) break;
- if (des) des += len;
- num += len;
- }
- if (des) (*des) = 0;
- return num;
- }
- }
这样用起来就更加简单了:
- const char* c = "你好世界";
- size_t n = nx::transform::utf(c, (wchar_t*)0);
完整代码请参考:
https://code.google.com/p/nixy/source/browse/trunk/nixycore/string/transform.h
更多内容请访问:http://darkc.at
http://blog.csdn.net/markl22222/article/details/19770505
UTF-8、UTF-16、UTF-32编码的相互转换(不使用现成的函数)的更多相关文章
- UTF-8、UTF-16、UTF-32编码的相互转换
最近在考虑写一个可以跨平台的通用字符串类,首先需要搞定的就是编码转换问题. vs默认保存代码文件,使用的是本地code(中文即GBK,日文即Shift-JIS),也可以使用带BOM的UTF-8.gcc ...
- Ansi、GB2312、GBK、Unicode(utf8、16、32)
关于ansi,一般默认为本地编码方式,中文应该是gb编码 他们之间的关系在这边文章里描写的很清楚:http://blog.csdn.net/ldanduo/article/details/820353 ...
- c# 字符串(含有汉字)转化为16进制编码(转)
public static string Str2Hex(string s) { string result = string.Empty; byte[] arrByte = System.Text. ...
- [原创]obj-c编程16:键值编码(KVC)
原文链接:obj-c编程16:键值编码(KVC) 我们可以借助obj-c中的键值编码(以后简称KVC,Key-Value Coding)来存取类的属性,通过指定所要访问的属性名字符串标示符,可以使用存 ...
- 外设位宽为8、16、32时,CPU与外设之间地址线的连接方法
有不少人问到:flash连接CPU时,根据不同的数据宽度,比如16位的NOR FLASH (A0-A19),处理器的地址线要(A1-A20)左移偏1位.为什么要偏1位? (全文有点晦涩,建议收藏本文对 ...
- .NET Core RSA 签名和验签(密钥为 16 进制编码)
使用 OpenSSL 生成公私钥对,命令: $ openssl genrsa -out rsa_1024_priv.pem $ openssl pkcs8 -topk8 -inform PEM -in ...
- MD5、SHA1加密java 16位32位
MD5.SHA1加密java 16位32位 import java.math.BigInteger; import java.security.MessageDigest; public class ...
- java byte数组与16进制间的相互转换
java byte数组与16进制间的相互转换 CreationTime--2018年6月11日15点34分 Author:Marydon 1.准备工作 import java.util.Array ...
- 【Canvas】绘制几何级数Geometric series曲线 y=1+1/2+1/4+1/8+1/16+1/32+1/64+....
相关资料:https://baike.baidu.com/item/%E5%87%A0%E4%BD%95%E7%BA%A7%E6%95%B0/112584?fr=aladdin 图线: 代码: < ...
随机推荐
- docker+jenkins的实现方式(ps.使用dockerfile的方式)!
继http://www.cnblogs.com/guilty/p/4747993.html之后. 前两天朋友问的,docker+jenkins整合. 我也没搞过,但是正好最近有空,我也很有兴趣,就搞一 ...
- IDEA切换git分支
查看当前所在分支 场景:在多人开发中,需要在主分支的基础上创建一些分支分配给小团队或个人去开发,然后小分支上的小功能开发完毕之后,再merge(合并)到主分支. 1.查看当前所在的分支 下图1.1中是 ...
- 通过 PHP OPcache 提升 Laravel 应用运行速度
什么是 OPcache 每一次执行 PHP 脚本的时候,该脚本都需要被编译成字节码,而 OPcache 可以对该字节码进行缓存,这样,下次请求同一个脚本的时候,该脚本就不需要重新编译,这极大节省了脚本 ...
- python if elif else 区别
if data_ori=='医疗': # 医疗 df = pd.read_excel(path_apply + 'apply/YS_ZY_HZSQ_样例.xls', encoding='gbk', e ...
- 在 Linux 启动或重启时执行命令与脚本
有时可能会需要在重启时或者每次系统启动时运行某些命令或者脚本.我们要怎样做呢?本文中我们就对此进行讨论. 我们会用两种方法来描述如何在 CentOS/RHEL 以及 Ubuntu 系统上做到重启或者系 ...
- HTTP Status 500 - java.lang.ClassNotFoundException: org.apache.jsp.register_jsp
你搜一下你的页面中是不是有<!---->的注释 去掉就好了 改成jsp的注释 1).JSP页面中的HTML注释 JSP页面中的HTML注释使用“<!—”和“-->”创建,它的具 ...
- ROS报错:IOError:[Errno 13]permission denied: /home/neousys/.ros/roscore-11311.pid"
在安装ROS后启动ROS,输入:roscore 时报错: 这个问题是由于该路径下ros文件权限造成的. 输入以下命令修改权限: sudo chmod -R ~/.ros/ 修改完成后再次输入rosco ...
- oracle函数 RPAD(c1,n[,c2])
[功能]在字符串c1的右边用字符串c2填充,直到长度为n时为止 [参数]C1 字符串 n 追加后字符总长度 c2 追加字符串,默认为空格 [返回]字符型 [说明]如果c1长度大于n,则返回c1左边n个 ...
- 现代IM系统中的消息系统架构 - 模型篇
前言 在架构篇中我们介绍了现代IM消息系统的架构,介绍了Timeline的抽象模型以及基于Timeline模型构建的一个支持『消息漫游』.『多端同步』和『消息检索』多种高级功能的消息系统的典型架构.架 ...
- kubernetes API 访问控制在阿里云容器服务(ACK)上的实践
提起K8s API的访问控制,很多同学应该都会想到RBAC,这是K8s用来做权限控制的方法,但是K8s对API的访问控制却不止于此,今天我们就来简单介绍下K8s的访问控制以及ACK如何利用这套方法提供 ...