# 编码

根据[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;
~~~~~~~~~~

可以在[DOM's Encoding](doc/stream.md)一节看到更详细的使用例子。

## 字符类型 {#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`及对应的流来在运行时设置内源／目的之编码。
