zhuanzi:http://blog.csdn.net/yulongli/article/details/23671785 分类: 基础知识2014-04-14 20:47 460人阅读 收藏 举报 在代码编写中遇到字符串的地方少不了需要转义。为何要转义、何时转义、如何转义这几个问题也让很多开发者困扰不已。而且,编码和转义关系也是非常密切的。一、为什么要转义。以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: -
-
-
-
-
- std::string escape(const std::string& sUCValue)
- {
- 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
- };
-
- const T_UC* bpos = (T_UC*)&sUCValue[0];
- const T_UC* epos = bpos + (sUCValue.size()/sizeof(T_UC));
-
- std::string sValue = __encodeBase(s_esc, bpos, epos, "%", "%u", "");
- return sValue;
- }
其中__encodeBase的实现如下:
- char* ITOX2(int x, char vx[2])
- {
- static char MAPX[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
- vx[0] = MAPX[(unsigned char)x>>4];
- vx[1] = MAPX[x&0x0F];
- return vx;
- }
-
- char* ITOX4(int x, char vx[4])
- {
- ITOX2((unsigned short)(x&0xFF00)>>8, vx);
- ITOX2(x&0xFF, vx+2);
- return vx;
- }
- std::string __encodeBase(const bool esc[256], const T_UC* bpos, const T_UC* epos, const char* prefix2, const char* prefix4, const char* subfix)
- {
- int bSize2 = strlen(prefix2);
- int bSize4 = strlen(prefix4);
- int eSize = strlen(subfix);
-
- char v2[16] = {0};
- char v4[16] = {0};
- char* p2 = v2+bSize2;
- char* p4 = v4+bSize4;
-
- memcpy(v2, prefix2, bSize2);
- memcpy(v4, prefix4, bSize4);
- memcpy(p2+2, subfix, eSize);
- memcpy(p4+4, subfix, eSize);
-
- int s2 = bSize2+2+eSize;
- int s4 = bSize4+4+eSize;
-
- std::string sValue((bSize4+eSize+4)*(epos-bpos), 0);
- char* tpos = &sValue[0];
-
- while (bpos < epos)
- {
- if (*bpos & 0xff00)
- {
- ITOX4(*bpos, p4);
- memcpy(tpos, v4, s4);
- tpos += s4;
- ++bpos;
- }
- else if (esc[*bpos & 0xff])
- {
- ITOX2(*bpos, p2);
- memcpy(tpos, v2, s2);
- tpos += s2;
- ++bpos;
- }
- else
- {
- *tpos++ = *bpos++;
- }
- }
- sValue.resize(tpos - &sValue[0]);
-
- return sValue;
- }
同理我们来实现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代码实现:
-
-
-
-
-
- std::string encodeURIComponent(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, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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
- };
-
- std::string sValue;
- sValue.reserve(sData.size() * 4);
-
- T_UTF8 *bpos = (T_UTF8*)&sData[0];
- T_UTF8 *epos = bpos + sData.size();
-
- while (bpos<epos)
- {
- if (!s_esc[*bpos & 0xff])
- {
- sValue += *bpos;
- }
- else
- {
- char vx[4]={'%', 0, 0, 0};
- ITOX2(*bpos, vx+1);
- sValue += vx;
- }
- bpos++;
- }
- return sValue;
- }
两者对应的decode函数分别如下: -
-
-
-
-
- std::string decodeURIComponent(const std::string& sData)
- {
- std::string sResult = sData;
- int x, y;
-
- for (x = 0, y = 0; sData[y]; x++, y++)
- {
- if((sResult[x] = sData[y]) == '%')
- {
- sResult[x] = x2c(&sData[y+1]);
- y += 2;
- }
- }
-
- return sResult.substr(0, x);
- }
-
-
-
-
-
-
- std::string unescape(const std::string& sData)
- {
- struct HEX
- {
- static bool isHex(T_UC ch)
- {
- return (ch>='0' && ch<='9' || ch>='A' && ch<='F' || ch>='a' && ch<='f');
- }
- };
-
-
- std::string sUCValue = UCS2(sData);
- std::string sValue(sUCValue.size(), 0);
-
- T_UC *ucbpos = (T_UC *)&sUCValue[0];
- T_UC *ucepos = ucbpos + sUCValue.size()/sizeof(T_UC);
-
- T_UC *ucbResult = (T_UC *)&sValue[0];
-
- #define HEXUCTOI(uc) ((uc >= 'a')? (uc - 'a' + 10) : (uc >= 'A') ? (uc - 'A' + 10) : (uc - '0'))
-
- while (ucbpos < ucepos)
- {
- if (*ucbpos != 0x25)
- {
- *ucbResult++ = *ucbpos++;
- }
- 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)))
- {
- *ucbResult++ = (HEXUCTOI(*(ucbpos+2))<<12) | (HEXUCTOI(*(ucbpos+3))<<8) | (HEXUCTOI(*(ucbpos+4))<<4) | (HEXUCTOI(*(ucbpos+5)));
- ucbpos=ucbpos+6;
- }
- else if ((ucepos>ucbpos+2) && HEX::isHex(*(ucbpos+1)) && HEX::isHex(*(ucbpos+2)) )
- {
- *ucbResult++ = (HEXUCTOI(*(ucbpos+1))<<4) | (HEXUCTOI(*(ucbpos+2)));
- ucbpos=ucbpos+3;
- }
- else
- {
- *ucbResult++ = *ucbpos++;
- }
- }
- sValue.resize((ucbResult-(T_UC *)&sValue[0])*sizeof(T_UC));
- return sValue;
- }
另外,还有json、xml、html的转义,后续再介绍。
|