中文字符编码的相互转换(五)

我们来了解一下JSON、XML、HTML的转义。

对JSON字符进行转义的原理跟上一节讨论的那几个函数不太一样。

在Javascript中的字符有多种表达形式:

1,ASCII码的8进制转义,16进制转义,比如换行符可以用”\x0A” 和 “\12” 表示 。
2,ASCII码里面特殊字符的转义,比如换行符还可以用”\n” 表示。
3,从开发者角度来看,Javascript中的所有字符都是Unicode字符,所以换行符还可以用”\u0012”来表示,其中0012是换行符的Unicode编码。

如果涉及到中文的处理,会发现如果统一采用第3种方式来对字符编码,会消除非ASCII码对编码环境的要求。

Javascript中有个方法JSON.stringify可以起到一定的辅助作用,帮助我们找到需要编码的字符。首先用shell命令得到JavaScript代码:
awk 'BEGIN{for(i=0;i<8;i++){for(j=0;j<16;j++){printf("%X\n", i*16+j)}}}' | awk '{p="";if(NR<=16)p="0";printf("console.log(\"%s\",\"\\x%s%s\", JSON.stringify(\"\\x%s%s\"));\n",$1, p, $1, p, $1)}’

得到结果,我们发现有些字符被转化为“\uxxxx”的格式,有些没有。我们的实现也按照这一规则。当然那些没有变成这样格式的字符我们也可以如此表示,但为了可读性以及节省输出字符的长度,我们还是用其他方式来表示。

其中,退格键(0x08,\b),制表符(0x09,\t),换行符(0x0a,\n),回车(0x0d,\r),换页符(0x0c,\f),双引号(0x22,“),反斜杠(0x5c,\)这7个符号需要特殊处理,其他的都不做处理。这几个字符处理完之后在我们的map中相对应的位置设置为0或是1都没有影响。其他字符在0x00-0x1F内的转为\uxxxx格式,在0x20-0x7F内的字符不做处理。在0x80-0x7F内的字符原则上可以不做处理,这里为了防止在其他平台上解析出问题,统一转为\uxxxx格式。

代码实现如下:

std::string CXCode::encodeJSONComponent(const std::string& sData)
{
     std::string sUCValue = UCS2(sData);
     T_UC* bpos = (T_UC*)&sUCValue[0];
     const T_UC* epos = bpos + (sUCValue.size()/sizeof(T_UC));

     T_UC * tUC = new T_UC[sData.size() * 4];
     T_UC * ptUC = tUC;

     while (bpos < epos)
     {
          if (*bpos == '\\' || *bpos== '\"' )
          {
               *ptUC++ = '\\';
               *ptUC++ = *bpos;
          }
          else if (*bpos == '\n')
          {
               *ptUC++ = '\\';
               *ptUC++ = 'n';
          }
          else if (*bpos == '\r')
          {
               *ptUC++ = '\\';
               *ptUC++ = 'r';
          }
          else if (*bpos == '\b')
          {
               *ptUC++ = '\\';
               *ptUC++ = 'b';
          }
          else if (*bpos == '\f')
          {
               *ptUC++ = '\\';
               *ptUC++ = 'f';
          }
          else if (*bpos == '\t')
          {
               *ptUC++ = '\\';
               *ptUC++ = 't';
          }
          else
          {
               *ptUC++ = *bpos;
          }
         
          ++bpos;
     }
     bpos = tUC;
     epos = ptUC;

     const static bool s_esc[256] =
     {
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
     };

     std::string sValue = __encodeBase(s_esc, bpos, epos, "\\u00", "\\u", "");

     if (tUC != NULL)
     {
          delete[] tUC;
     }
            
     if (CXCode::GetCharSet()==CXCode::CHARSET_UCS2)
     {
          CXCode x2(CXCode::CHARSET_UTF8);
          return UCS2(sValue);
     }

     return sValue;
}


再来看xml和html,他们两者都有表示转义字符的方法:实体名字和实体编号。比如“<”,用实体名字表示就是“&lt;”用实体编号表示是“&#60;”。当然实体编号还可以用16进制表示为“&#x3c;”
用实体(Entity)名字的好处是比较好理解,但是其劣势在于并不是所有的浏览器都支持最新的Entity名字。而实体(Entity)编号,各种浏览器都能处理。

我们这里为了兼容性,统一用实体编号来进行转义。

xml中有几个字符必须要转义,他们是 & < > " ' ,在HTML中还有个空格。他们的实体名字和16进制编码分别为


&  &amp;   0x26 
<  &lt;    0x3C
>  &gt;  0x3E
“  &quot;  0x22
‘  &apos;   0x27
   &nbsp;  0x20

除了这几个符号之外,其他的符号理论上来说不需要做特殊处理。但是有些不可见字符,还有回车换行符等,最好还是转义一下,反正转义多了不会出错,只会带来人眼识别上的困难。所以最终我们的代码实现如下:

std::string encodeXMLComponent(const std::string& sData)
{
     const static bool s_esc[256] =
     {
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
          1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
     };

     std::string sUCValue = UCS2(sData);
     T_UC* bpos = (T_UC*)&sUCValue[0];
     const T_UC* epos = bpos + (sUCValue.size()/sizeof(T_UC));

     while (bpos < epos)
     {
          if (*bpos < 32 && *bpos!=13 && *bpos!=10)
          {
               *bpos = T_UC('?');
          }
          ++bpos;
     }
     bpos = (T_UC*)&sUCValue[0];

     std::string sValue = __encodeBase(s_esc, bpos, epos, "&#x", "&#x", ";");

     return sValue;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值