中文字符編碼的相互轉換(五)

我們來了解一下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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章