這是前幾天偶爾碰到的問題, 發現跟本身的對python理解不一致, 雖然文檔上已明確說明不支持的,可是爲何就不支持這麼個語法?python
Even in a raw literal, quotes can be escaped with a backslash, but the backslash remains in the result; for example, r""" is a valid string literal consisting of two characters: a backslash and a double quote; r"" is not a valid string literal (even a raw string cannot end in an odd number of backslashes). Specifically, a raw literal cannot end in a single backslash (since the backslash would escape the following quote character). Note also that a single backslash followed by a newline is interpreted as those two characters as part of the literal, not as a line continuation.bash
"\"在python字符串中算特殊字符, 起轉義做用. \n轉義n變回車, \\轉義\等等,這個應該沒有問題. 在正常狀況下a="\" 是存在語法問題, 由於\轉義了右邊的",因此字符串少一個右引號,錯誤緣由很明顯了. 可是python(很做死的)有個r操做符(全稱 原始字符串操做符), 就是對字符串內的內容按字面意思解析,不作特殊處理,因此若是a=r"\n" 輸出的就是"\\n",而不是回車,爲何是"\\n", goto 1 再看看. 那麼如今問題來了, 按r的功能, a=r"\", 應該沒問題,由於在r的場子裏,\是不能轉義",可是如今仍是給你一個語法錯誤,syntaxError.spa
其實我剛纔說錯了, 這不是一個語法錯誤, 這tmd是個詞法錯誤.(略裝B的說法,語法詞法概念模糊的能夠網上稍微看下). 爲何這麼說, 跟了下python的源碼,發現問題出在Parser\tokenizer.c內, python對字符串對象的詞法解析部分.代碼以下:code
/* String */
letter_quote:
if (c == '\'' || c == '"') {
Py_ssize_t quote2 = tok->cur - tok->start + 1;
int quote = c;
int triple = 0;
int tripcount = 0;
for (;;) {
c = tok_nextc(tok);
if (c == '\n') {
if (!triple) {
tok->done = E_EOLS;
tok_backup(tok, c);
return ERRORTOKEN;
}
tripcount = 0;
tok->cont_line = 1; /* multiline string. */
}
else if (c == EOF) {
if (triple)
tok->done = E_EOFS;
else
tok->done = E_EOLS;
tok->cur = tok->inp;
return ERRORTOKEN;
}
else if (c == quote) {
tripcount++;
if (tok->cur - tok->start == quote2) {
c = tok_nextc(tok);
if (c == quote) {
triple = 1;
tripcount = 0;
continue;
}
tok_backup(tok, c);
}
if (!triple || tripcount == 3)
break;
}
else if (c == '\\' ) {
tripcount = 0;
c = tok_nextc(tok);
if (c == EOF) {
tok->done = E_EOLS;
tok->cur = tok->inp;
return ERRORTOKEN;
}
}
else
tripcount = 0;
}
*p_start = tok->start;
*p_end = tok->cur;
return STRING;
}
複製代碼
其實蠻簡單的. 幾個地方須要扯一下:對象
Ok,剩下的有點c基礎的就能夠啃了. 這裏簡單分析下, if (c == ''' || c == '"')當前字符是'或"進入字符串對象的詞法解析過程了,因此python支持'和"兩種引號字符。在for循環一共有4個else if,其實就是說明python的字符串有4類特殊字符。token
#define E_EOFS 23 /* EOF in triple-quoted string */
#define E_EOLS 24 /* EOL in single-quoted string */
複製代碼
這搞的好像在單引號模式下,不能輸回車同樣的,可是有點python經驗的都知道行尾加個\就能夠輸入回車, 在下一行重頭再來.是的, 請記住, 要輸入\. 從中也能夠看出,在三引號模式下, 回車是能夠隨便輸的.ip
EOF文件尾(輸入流停水了). 這個簡單除暴了,根據當前模式,設置下錯誤碼,而後返回失敗。ci
quote引號. 先跳過if (tok->cur - tok->start == quote2). 單看if (!triple || tripcount == 3). 若是沒在三引號模式下,又"摸到"個quote,那當前這個字符串詞法解析就完了, break出去,一個字符串就ko了(尼瑪,略簡單的說). 回過頭說下if (tok->cur - tok->start == quote2) 這個,就是判斷三引號的地方了.跟本文核心內容無關, 就不扯了哈.rem
'\',終於到這貨了. 處理也很簡單, 核心就是c = tok_nextc(tok), 把的下個字符從輸入流裏取了,其餘什麼都無論. 由於還在詞法階段,無法解析轉義字符.那麼如今不少問題找到答案:文檔
ok, python對string對象的詞法解析就這樣了. 那麼扯了這麼多, 還沒提r操做符,在詞法階段,他的處理很偷懶,代碼以下:
case 'r':
case 'R':
c = tok_nextc(tok);
if (c == '"' || c == '\'') { goto letter_quote; } 複製代碼
尼瑪, 就是一個goto 到字符串解析部分了. (?:啥表情也不作,就把活甩給別人了? r:詞法分析階段,老子能幹嗎!!!). 由於r原始操做符, 應該屬於python語法上內容, 因此詞法分析階段,他的處理方式就是常規字符串的處理方式(r:贊一個). 因此面對"\"時,也要報個Syntax EOL in single-quoted string.
其實解決這個問題仍是很簡單的, 加個標示變量
int bInRMode = 0;
case 'r':
case 'R':
c = tok_nextc(tok);
if (c == '"' || c == '\'') { bInRMode = 1; //設置下標示位 goto letter_quote; } else if (c == '\\' ) { tripcount = 0; c = tok_nextc(tok); if (c == EOF) { tok->done = E_EOLS; tok->cur = tok->inp; return ERRORTOKEN; } if( c !='\n' && bInRMode){ tok_backup(tok ,c); } } 複製代碼
在解析\時, 須要特殊處理下. 應爲r模式下, \應該是普通字符,不該該有取下個字符的功能,因此我加個代碼把取的字符又塞回去了. 原本在else if (c == '\')這裏能夠直接處理的,可是若是不對回車特殊處理,那單引號模式下就不能經過\輸回車了,因此對\後接\n,又不採用r的功能了.