2015-02-17 16:36:15 +08:00
# 编码
根据[ECMA-404 ](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf ):
> (in Introduction) JSON text is a sequence of Unicode code points.
>
> 翻译: JSON文本是Unicode码点的序列。
较早的[RFC4627 ](http://www.ietf.org/rfc/rfc4627.txt )申明:
> (in §3) JSON text SHALL be encoded in Unicode. The default encoding is UTF-8.
>
> 翻译: JSON文本应该以Unicode编码。缺省的编码为UTF-8。
> (in §6) JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON is written in UTF-8, JSON is 8bit compatible. When JSON is written in UTF-16 or UTF-32, the binary content-transfer-encoding must be used.
>
> 翻译: JSON可使用UTF-8、UTF-16或UTF-18表示。当JSON以UTF-8写入, 该JSON是8位兼容的。当JSON以UTF-16或UTF-32写入, 就必须使用二进制的内容传送编码。
RapidJSON支持多种编码。它也能检查JSON的编码, 以及在不同编码中进行转码。所有这些功能都是在内部实现, 无需使用外部的程序库( 如[ICU ](http://site.icu-project.org/ ))。
[TOC]
# Unicode {#Unicode}
根据 [Unicode的官方网站 ](http://www.unicode.org/standard/translations/t-chinese.html ):
>Unicode给每个字符提供了一个唯一的数字,
不论是什么平台、
不论是什么程序、
不论是什么语言。
这些唯一数字称为码点( code point) , 其范围介乎`0x0` 至`0x10FFFF` 之间。
## Unicode转换格式 {#UTF}
存储Unicode码点有多种编码方式。这些称为Unicode转换格式( Unicode Transformation Format, UTF) 。RapidJSON支持最常用的UTF, 包括:
* UTF-8: 8位可变长度编码。它把一个码点映射至1至4个字节。
* UTF-16: 16位可变长度编码。它把一个码点映射至1至2个16位编码单元( 即2至4个字节) 。
* UTF-32: 32位固定长度编码。它直接把码点映射至单个32位编码单元( 即4字节) 。
对于UTF-16及UTF-32来说, 字节序( endianness) 是有影响的。在内存中, 它们通常都是以该计算机的字节序来存储。然而, 当要储存在文件中或在网上传输, 我们需要指明字节序列的字节序, 是小端( little endian, LE) 还是大端( big-endian, BE) 。
RapidJSON通过`rapidjson/encodings.h` 中的struct去提供各种编码:
~~~~~~~~~~cpp
namespace rapidjson {
template< typename CharType = char >
struct UTF8;
template< typename CharType = wchar_t >
struct UTF16;
template< typename CharType = wchar_t >
struct UTF16LE;
template< typename CharType = wchar_t >
struct UTF16BE;
template< typename CharType = unsigned >
struct UTF32;
template< typename CharType = unsigned >
struct UTF32LE;
template< typename CharType = unsigned >
struct UTF32BE;
} // namespace rapidjson
~~~~~~~~~~
对于在内存中的文本,我们正常会使用`UTF8` 、`UTF16` 或`UTF32` 。对于处理经过I/O的文本, 我们可使用`UTF8` 、`UTF16LE` 、`UTF16BE` 、`UTF32LE` 或`UTF32BE` 。
当使用DOM风格的API, `GenericValue<Encoding>` 及`GenericDocument<Encoding>` 里的`Encoding` 模板参数是用于指明内存中存储的JSON字符串使用哪种编码。因此通常我们会在此参数中使用`UTF8` 、`UTF16` 或`UTF32` 。如何选择, 视乎应用软件所使用的操作系统及其他程序库。例如, Windows API使用UTF-16表示Unicode字符, 而多数的Linux发行版本及应用软件则更喜欢UTF-8。
使用UTF-16的DOM声明例子:
~~~~~~~~~~cpp
typedef GenericDocument< UTF16 < > > WDocument;
typedef GenericValue< UTF16 < > > WValue;
~~~~~~~~~~
2015-04-07 15:18:05 +08:00
可以在[DOM's Encoding ](doc/stream.md )一节看到更详细的使用例子。
2015-02-17 16:36:15 +08:00
## 字符类型 {#CharacterType}
从之前的声明中可以看到,每个编码都有一个`CharType` 模板参数。这可能比较容易混淆,实际上,每个`CharType` 存储一个编码单元, 而不是一个字符( 码点) 。如之前所谈及, 在UTF-8中一个码点可能会编码成1至4个编码单元。
对于`UTF16(LE|BE)` 及`UTF32(LE|BE)` 来说,`CharType` 必须分别是一个至少2及4字节的整数类型。
注意C++11新添了`char16_t` 及`char32_t` 类型,也可分别用于`UTF16` 及`UTF32` 。
## AutoUTF {#AutoUTF}
上述所介绍的编码都是在编译期静态挷定的。换句话说,使用者必须知道内存或流之中使用了哪种编码。然而,有时候我们可能需要读写不同编码的文件,而且这些编码需要在运行时才能决定。
`AutoUTF` 是为此而设计的编码。它根据输入或输出流来选择使用哪种编码。目前它应该与`EncodedInputStream` 及`EncodedOutputStream` 结合使用。
## ASCII {#ASCII}
虽然JSON标准并未提及[ASCII ](http://en.wikipedia.org/wiki/ASCII ), 有时候我们希望写入7位的ASCII JSON, 以供未能处理UTF-8的应用程序使用。由于任JSON都可以把Unicode字符表示为`\uXXXX` 转义序列, JSON总是可用ASCII来编码。
以下的例子把UTF-8的DOM写成ASCII的JSON:
~~~~~~~~~~cpp
using namespace rapidjson;
Document d; // UTF8< >
// ...
StringBuffer buffer;
Writer< StringBuffer , Document::EncodingType , ASCII < > > writer(buffer);
d.Accept(writer);
std::cout < < buffer.GetString ( ) ;
~~~~~~~~~~
ASCII可用于输入流。当输入流包含大于127的字节, 就会导致`kParseErrorStringInvalidEncoding` 错误。
ASCII *不能* 用于内存(`Document` 的编码,或`Reader` 的目标编码), 因为它不能表示Unicode码点。
# 校验及转码 {#ValidationTranscoding}
当RapidJSON解析一个JSON时, 它能校验输入JSON, 判断它是否所标明编码的合法序列。要开启此选项, 请把`kParseValidateEncodingFlag` 加入`parseFlags` 模板参数。
若输入编码和输出编码并不相同,`Reader` 及`Writer` 会算把文本转码。在这种情况下,并不需要`kParseValidateEncodingFlag` ,因为它必须解码输入序列。若序列不能被解码,它必然是不合法的。
## 转码器 {#Transcoder}
虽然RapidJSON的编码功能是为JSON解析/ 生成而设计, 使用者也可以“滥用”它们来为非JSON字符串转码。
以下的例子把UTF-8字符串转码成UTF-16:
~~~~~~~~~~cpp
#include "rapidjson/encodings.h"
using namespace rapidjson;
const char* s = "..."; // UTF-8 string
StringStream source(s);
GenericStringBuffer< UTF16 < > > target;
bool hasError = false;
while (source.Peak() != '\0')
if (!Transcoder::Transcode< UTF8 < > , UTF16< > >(source, target)) {
hasError = true;
break;
}
if (!hasError) {
const wchar_t* t = target.GetString();
// ...
}
~~~~~~~~~~
你也可以用`AutoUTF` 及对应的流来在运行时设置内源/目的之编码。