分享

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

 海漩涡 2014-12-24

zhuanzi:http://blog.csdn.net/yulongli/article/details/23671785

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

分类: 基础知识 460人阅读 评论(0) 收藏 举报
在代码编写中遇到字符串的地方少不了需要转义。为何要转义、何时转义、如何转义这几个问题也让很多开发者困扰不已。而且,编码和转义关系也是非常密切的。

一、为什么要转义。

以C语言做例子,我想声明一个char字符,该字符表示一个换行。但ASCII码中没有表示换行的文字符号,所以必须用转义字符来表示。于是我们可以这样定义:
char c1=‘\n’;
其中’\n’ 用两个ASCII码字符表示了一个换行符。编译器在遇到反斜杠时,会将其与后面的字符合在一起当作一个字符来处理。

再一种情况,这次我想声明的char字符表示一个单引号(‘)。但是在C语言中单引号是有特殊含义的,两个单引号之间是一个字符。如果中间再加一个单引号,那么编译器就没法正确理解这几个单引号的含义。所以,我们需要如下定义:
char c2=‘\’’;
编译器遇到的第一个单引号是C语法字符,遇到的第二个单引号因为是在反斜杠之后,会被编译器认定是真正的单引号,遇到的第三个单引号同第一个一样,是C语法字符。

第三种情况,因为ASCII码只能表示0-127的字符,那大于128的字符如何表示呢?比如大小为255的字符我么可以这么表示:
char c3=‘\xff’;

现在我们来总结使用转义字符的原因,主要是有两个:
1,有些字符没有文字字符表示,比如ASCII中的空字符、换行符等,还有些字符超出了ASCII码的范围,即那些大于128的字符,这些字符需要用转义字符来表示。
2,某些特定字符在编程语言中有特殊用途,在这些编程语言中就失去了字符原有的含义。如C语言中的引号,HTML中的<等。

二、何时转义

转义的目的有两个:
1,通过转义字符来表示无法表示的内容。
2,通过转义将原本的字符意义改变。在实际使用中处于安全考虑会做。如在执行SQL语句时,若某些数据是外来输入,则需要将这些数据进行转义,以防止注入攻击。

转义操作发生在输入时。为了让输入内容本义改变而做转义操作。实际使用中很多需要在输出时做转义,但这些输出也只是为了成为别人的输入。

三、如何转义
这里我拿web中最常用的三个转义方法来做例子,分别是escape,encodeURI,encodeURIComponent。在此我们用C语言来实现这几个方法,达到对其完全理解的目的。

这三个方法是在web开发中最常用的对字符串编码的函数,均是将特殊字符转换成为%xx格式的编码(xx等于该字符的16进制编码),但还是有些不太一样。

encodeURI,encodeURIComponent:将非ASCII码转为UTF-8格式,然后特殊字符用16进制表示。特殊字符是非ASCII码以及ASCII码中需要转义的字符。两者的区别是字符集的不同。前者比后者要少一点。
escape:先将ASCII码转为UCS-2格式,然后特殊字符用16进制表示。

非ASCII码肯定是需要转义的字符,ASCII码中的待转义字符可以利用Javascript来辅助找到。在Shell中通过以下命令可以得到判断128个ASCII码是否为待转义字符的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\",escape(\"\\x%s%s\")==\"\\x%s%s\");\n",$1, p, $1, p, $1)}'


可以得到128行代码:
console.log("0",escape("\x00")=="\x00");
console.log("1",escape("\x01")=="\x01");
console.log("2",escape("\x02")=="\x02");
console.log("3",escape("\x03")=="\x03");
console.log("4",escape("\x04")=="\x04");
console.log("5",escape("\x05")=="\x05");
console.log("6",escape("\x06")=="\x06");
console.log("7",escape("\x07")=="\x07");
console.log("8",escape("\x08")=="\x08");
console.log("9",escape("\x09")=="\x09");
console.log("A",escape("\x0A")=="\x0A");
console.log("B",escape("\x0B")=="\x0B");
console.log("C",escape("\x0C")=="\x0C");
console.log("D",escape("\x0D")=="\x0D");
console.log("E",escape("\x0E")=="\x0E");
console.log("F",escape("\x0F")=="\x0F");


在Chrome下面打开控制台,执行以上代码,可以得到如下:

0 false
1 false 
2 false
3 false 
4 false 
5 false 


其中false代表需要转义的字符,true代表无需转义的字符,这样我就可以得到128个字符中转义成%XX的字符数了。当然为了保险起见,这些被转义的字符是否真的变成%XX,还可以通过这条命令的结果在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("if(escape(\"\\x%s%s\")!=\"\\x%s%s\")console.log(\"%s\", escape(\"\\x%s%s\")==\"%%%s%s\");\n", p, $1, p, $1, $1, p, $1, p, $1)}’

经检验,与我们开始的判断完全吻合,大家可以自己试一下。现在,我们用CPP代码来实现escape:

  1. /*!  
  2.  * \brief 将UCS-2编码文本转义 
  3.  * \param[in] sUCValue: sUCValue UCS2格式字符串 
  4.  * \return string 转换后的字符串 
  5.  */    
  6. std::string escape(const std::string& sUCValue)  
  7. {  
  8.      const static bool s_esc[256] =  
  9.      {  
  10.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  11.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  12.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0,  
  13.           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,  
  14.           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  
  15.           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  
  16.           1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  
  17.           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,  
  18.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  19.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  20.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  21.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  22.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  23.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  24.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  25.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1  
  26.      };  
  27.   
  28.      const T_UC* bpos = (T_UC*)&sUCValue[0];  
  29.      const T_UC* epos = bpos + (sUCValue.size()/sizeof(T_UC));  
  30.   
  31.      std::string sValue = __encodeBase(s_esc, bpos, epos, "%""%u""");  
  32.      return sValue;  
  33. }  

其中__encodeBase的实现如下:

  1. char* ITOX2(int x, char vx[2])  
  2. {  
  3.      static char MAPX[16] = { '0''1''2''3''4''5''6''7''8''9''A''B''C''D''E''F' };  
  4.      vx[0] = MAPX[(unsigned char)x>>4];  
  5.      vx[1] = MAPX[x&0x0F];  
  6.      return vx;  
  7. }  
  8.   
  9. char* ITOX4(int x, char vx[4])  
  10. {  
  11.      ITOX2((unsigned short)(x&0xFF00)>>8, vx);  
  12.      ITOX2(x&0xFF, vx+2);  
  13.      return vx;  
  14. }  
  15. std::string __encodeBase(const bool esc[256], const T_UC* bpos, const T_UC* epos, const char* prefix2, const char* prefix4, const char* subfix)  
  16. {  
  17.      int bSize2 = strlen(prefix2);  
  18.      int bSize4 = strlen(prefix4);  
  19.      int eSize = strlen(subfix);  
  20.       
  21.      char v2[16] = {0};  
  22.      char v4[16] = {0};  
  23.      char* p2 = v2+bSize2;  
  24.      char* p4 = v4+bSize4;  
  25.   
  26.      memcpy(v2, prefix2, bSize2);  
  27.      memcpy(v4, prefix4, bSize4);  
  28.      memcpy(p2+2, subfix, eSize);  
  29.      memcpy(p4+4, subfix, eSize);  
  30.   
  31.      int s2 = bSize2+2+eSize;  
  32.      int s4 = bSize4+4+eSize;  
  33.   
  34.      std::string sValue((bSize4+eSize+4)*(epos-bpos), 0);  
  35.      char* tpos = &sValue[0];  
  36.   
  37.      while (bpos < epos)  
  38.      {  
  39.           if (*bpos & 0xff00)  
  40.           {  
  41.                ITOX4(*bpos, p4);  
  42.                memcpy(tpos, v4, s4);  
  43.                tpos += s4;  
  44.                ++bpos;  
  45.           }  
  46.           else if (esc[*bpos & 0xff])  
  47.           {  
  48.                ITOX2(*bpos, p2);  
  49.                memcpy(tpos, v2, s2);  
  50.                tpos += s2;  
  51.                ++bpos;  
  52.           }  
  53.           else  
  54.           {  
  55.                *tpos++ = *bpos++;  
  56.           }  
  57.      }  
  58.      sValue.resize(tpos - &sValue[0]);  
  59.   
  60.      return sValue;  
  61. }  

同理我们来实现encodeURIComponent。先得到表示256个字符是否需要转义的状态map,通过以下结果得到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\",encodeURIComponent(\"\\x%s%s\")==\"\\x%s%s\");\n",$1, p, $1, p, $1)}'


CPP代码实现:

  1. /*!  
  2.  * \brief 将UTF-8编码文本转义 
  3.  * \param[in] sData: utf-8格式字符串 
  4.  * \return string 转换后的字符串 
  5.  */    
  6. std::string encodeURIComponent(const std::string& sData)  
  7. {  
  8.      const static bool s_esc[256] =  
  9.      {  
  10.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  11.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  12.           1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1,  
  13.           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,  
  14.           1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  
  15.           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  
  16.           1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  
  17.           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  
  18.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  19.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  20.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  21.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  22.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  23.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  24.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  
  25.           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1  
  26.      };  
  27.   
  28.      std::string sValue;  
  29.      sValue.reserve(sData.size() * 4);  
  30.   
  31.      T_UTF8 *bpos = (T_UTF8*)&sData[0];  
  32.      T_UTF8 *epos = bpos + sData.size();  
  33.       
  34.      while (bpos<epos)  
  35.      {  
  36.           if (!s_esc[*bpos & 0xff])  
  37.           {  
  38.                sValue += *bpos;  
  39.           }  
  40.           else  
  41.           {  
  42.                char vx[4]={'%', 0, 0, 0};  
  43.                ITOX2(*bpos, vx+1);  
  44.                sValue += vx;  
  45.           }  
  46.           bpos++;  
  47.      }      
  48.      return sValue;  
  49. }  

两者对应的decode函数分别如下:

  1. /*!  
  2.  * \brief 将文本解码成utf-8 
  3.  * \param[in] sData, URIComponent之后的字符串 
  4.  * \return string 转换后的字符串,utf-8格式 
  5.  */    
  6. std::string decodeURIComponent(const std::string& sData)  
  7. {  
  8.      std::string sResult = sData;  
  9.      int x, y;  
  10.   
  11.      for (x = 0, y = 0; sData[y]; x++, y++)  
  12.      {  
  13.           if((sResult[x] = sData[y]) == '%')  
  14.           {  
  15.                sResult[x] = x2c(&sData[y+1]);  
  16.                y += 2;  
  17.           }  
  18.      }  
  19.       
  20.      return sResult.substr(0, x);  
  21. }  
  22.   
  23. /*!  
  24.  * \brief 将文本解码成unicode 
  25.  * \param[in] sData, escape之后的字符串 
  26.  * \return string 转换后的字符串,unicode格式 
  27.  */    
  28. std::string unescape(const std::string& sData)  
  29. {  
  30.      struct HEX  
  31.      {  
  32.           static bool isHex(T_UC ch)  
  33.           {  
  34.                return (ch>='0' && ch<='9' || ch>='A' && ch<='F' || ch>='a' && ch<='f');  
  35.           }  
  36.      };  
  37.   
  38.   
  39.      std::string sUCValue = UCS2(sData);  
  40.      std::string sValue(sUCValue.size(), 0);  
  41.   
  42.      T_UC *ucbpos = (T_UC *)&sUCValue[0];  
  43.      T_UC *ucepos = ucbpos + sUCValue.size()/sizeof(T_UC);  
  44.   
  45.      T_UC *ucbResult =  (T_UC *)&sValue[0];  
  46.   
  47.      #define HEXUCTOI(uc) ((uc >= 'a')? (uc - 'a' + 10) : (uc >= 'A') ? (uc - 'A' + 10) : (uc - '0'))  
  48.   
  49.      while (ucbpos < ucepos)  
  50.      {  
  51.           if (*ucbpos != 0x25)          // %  
  52.           {  
  53.                *ucbResult++ = *ucbpos++;  
  54.           }  
  55.           else if((*(ucbpos+1)=='u' || *(ucbpos+1)=='U') && (ucepos>ucbpos+5) && HEX::isHex(*(ucbpos+2)) && HEX::isHex(*(ucbpos+3)) && HEX::isHex(*(ucbpos+4)) && HEX::isHex(*(ucbpos+5)))  
  56.           {  
  57.                *ucbResult++ = (HEXUCTOI(*(ucbpos+2))<<12) | (HEXUCTOI(*(ucbpos+3))<<8) | (HEXUCTOI(*(ucbpos+4))<<4) | (HEXUCTOI(*(ucbpos+5)));  
  58.                ucbpos=ucbpos+6;  
  59.           }  
  60.           else if ((ucepos>ucbpos+2) && HEX::isHex(*(ucbpos+1)) && HEX::isHex(*(ucbpos+2)) )  
  61.           {  
  62.                *ucbResult++ = (HEXUCTOI(*(ucbpos+1))<<4) | (HEXUCTOI(*(ucbpos+2)));  
  63.                ucbpos=ucbpos+3;  
  64.           }  
  65.           else  
  66.           {  
  67.                *ucbResult++ = *ucbpos++;  
  68.           }  
  69.      }  
  70.      sValue.resize((ucbResult-(T_UC *)&sValue[0])*sizeof(T_UC));  
  71.      return sValue;  
  72. }  

另外,还有json、xml、html的转义,后续再介绍。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多