Python r”\" SyntaxError 問(wèn)題分析

這是前幾天偶爾碰到的問(wèn)題, 發(fā)現(xiàn)跟自己的對(duì)python理解不一致, 于是網(wǎng)上搜了下, 說(shuō)是Python的Bug(不過(guò)文檔上是有說(shuō)過(guò)說(shuō)不支持的) 正好最近在看python源碼, 于是就通過(guò)源碼分析了下.

1. 首先,說(shuō)下問(wèn)題:

""在python字符串中算特殊字符, 起轉(zhuǎn)義作用, \n轉(zhuǎn)義n變回車(chē), \轉(zhuǎn)義\等等,這個(gè)應(yīng)該沒(méi)啥問(wèn)題. 那么正常情況下a="" 是存在語(yǔ)法問(wèn)題, 因?yàn)閈轉(zhuǎn)義了右邊的",所以字符串少一個(gè)右括號(hào),錯(cuò)誤原因很明顯了. 但是python(很作死的)有個(gè)r操作符(全稱 原始字符串操作符), 就是對(duì)字符串內(nèi)的內(nèi)容按字面意思解析,不做特殊處理,所以如果a=r"\n" 輸出的就是"\n",而不是回車(chē),為什么是"\", goto 1 再看看. 那么現(xiàn)在問(wèn)題來(lái)了, 按r的功能, a=r"", 應(yīng)該沒(méi)問(wèn)題,因?yàn)樵趓的場(chǎng)子里,\是不能轉(zhuǎn)義",但是現(xiàn)在還是給你一個(gè)語(yǔ)法錯(cuò)誤,syntaxError.

2. 然后,我想說(shuō):

其實(shí)我剛才說(shuō)錯(cuò)了, 這不是一個(gè)語(yǔ)法錯(cuò)誤, 這tmd是個(gè)詞法錯(cuò)誤.(略裝B的說(shuō)法,語(yǔ)法詞法概念模糊的可以網(wǎng)上稍微看下). 為什么這么說(shuō), 跟了下python的源碼,發(fā)現(xiàn)問(wèn)題出在Parser\tokenizer.c內(nèi), python對(duì)字符串對(duì)象的詞法解析部分.代碼如下:

/* 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;  
    }  

其實(shí)蠻簡(jiǎn)單的.
幾個(gè)地方需要扯一下:

  • 是letter_quote:這個(gè)跳轉(zhuǎn)標(biāo)簽,請(qǐng)關(guān)注之,最后揭曉.
  • tok_nextc從輸入流中獲取下一個(gè)字符.
  • tok_backup將字符放回輸入流去.
  • triple 和tripcount這兩個(gè)變量跟三引號(hào)處理有關(guān),triple標(biāo)記現(xiàn)在是否處于三引號(hào)模式, 等于1時(shí),說(shuō)明當(dāng)前處于三引號(hào)模式, tripcount記錄連續(xù)的引號(hào)數(shù).

Ok,剩下的有點(diǎn)c基礎(chǔ)的就可以啃了.
這里簡(jiǎn)單分析下, if (c == ''' || c == '"')當(dāng)前字符是'或"進(jìn)入字符串對(duì)象的詞法解析過(guò)程了,所以python支持'和"兩種引號(hào)字符。在for循環(huán)一共有4個(gè)else if,其實(shí)就是說(shuō)明python的字符串有4類(lèi)特殊字符。

  1. \n回車(chē). 如果在非三引號(hào)模式下,檢測(cè)到回車(chē)后,設(shè)置下相應(yīng)的錯(cuò)誤碼, 然后返回失敗, 相關(guān)的錯(cuò)誤定義如下:
#define E_EOFS      23  /* EOF in triple-quoted string */  
#define E_EOLS      24  /* EOL in single-quoted string */  

這搞的好像在單引號(hào)模式下,不能輸回車(chē)試的,但是有點(diǎn)python經(jīng)驗(yàn)的都知道行尾加個(gè)\就可以輸入回車(chē), 在下一行重頭再來(lái).是的, 請(qǐng)記住, 要輸入#. 從中也可以看出,在三引號(hào)模式下, 回車(chē)是可以隨便輸?shù)?

  1. EOF文件尾(輸入流停水了). 這個(gè)簡(jiǎn)單除暴了,根據(jù)當(dāng)前模式,設(shè)置下錯(cuò)誤碼,然后返回失敗。

  2. quote引號(hào). 先跳過(guò)if (tok->cur - tok->start == quote2). 單看if (!triple || tripcount == 3). 如果沒(méi)在三引號(hào)模式下,又"摸到"個(gè)quote,那當(dāng)前這個(gè)字符串詞法解析就完了, break出去,一個(gè)字符串就ko了(尼瑪,略簡(jiǎn)單的說(shuō)). 回過(guò)頭說(shuō)下if (tok->cur - tok->start == quote2) 這個(gè),就是判斷三引號(hào)的地方了.跟本文核心內(nèi)容無(wú)關(guān), 就不扯了哈.

  3. '\',終于到這貨了. 處理也很簡(jiǎn)單, 核心就是c = tok_nextc(tok), 把的下個(gè)字符從輸入流里取了,其他什么都不管. 因?yàn)檫€在詞法階段,沒(méi)法解析轉(zhuǎn)義字符.那么現(xiàn)在很多問(wèn)題找到答案:

  • 加個(gè)\,單行模式可以輸入回車(chē)了,因?yàn)閜ython在解析到一個(gè)\后,直接把后面的回車(chē)從輸入流里取出來(lái)了, 同時(shí)也說(shuō)明,回車(chē)一定要緊跟\后面,因?yàn)閈只取他后面的一個(gè)字符.
  • ""為什么失敗, 應(yīng)為解析到\后,\把"取走了.那么下面的解析時(shí),面對(duì)的就是\n,所以Syntax EOL in single-quoted string

ok, python對(duì)string對(duì)象的詞法解析就這樣了. 那么扯了這么多, 還沒(méi)提r操作符,在詞法階段,他的處理很偷懶,代碼如下:

case 'r':  
case 'R':  
    c = tok_nextc(tok);  
    if (c == '"' || c == '\'')  
    {  
        goto letter_quote;  
    }  

尼瑪, 就是一個(gè)goto 到字符串解析部分了. (?:啥表情也不做,就把活甩給別人了? r:詞法分析階段,老子能干嘛!!!). 因?yàn)閞原始操作符, 應(yīng)該屬于python語(yǔ)法上內(nèi)容, 所以詞法分析階段,他的處理方式就是常規(guī)字符串的處理方式(r:贊一個(gè)). 所以面對(duì)""時(shí),也要報(bào)個(gè)Syntax EOL in single-quoted string.

3. 后話

其實(shí)解決這個(gè)問(wèn)題還是很簡(jiǎn)單的, 加個(gè)標(biāo)示變量

int bInRMode = 0;  

case 'r':  
case 'R':  
    c = tok_nextc(tok);  
    if (c == '"' || c == '\'')  
    {  
        bInRMode = 1; //設(shè)置下標(biāo)示位  
        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);  
    }  
}  

在解析\時(shí), 需要特殊處理下. 應(yīng)為r模式下, \應(yīng)該是普通字符,不應(yīng)該有取下個(gè)字符的功能,所以我加個(gè)代碼把取的字符又塞回去了. 本來(lái)在else if (c == '\')這里可以直接處理的,但是如果不對(duì)回車(chē)特殊處理,那單引號(hào)模式下就不能通過(guò)\輸回車(chē)了,所以對(duì)\后接\n,又不采用r的功能了.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 基礎(chǔ)命令 主要的命令和快捷鍵 Linux系統(tǒng)命令由三部分組成:cmd + [options]+[operation...
    485b1aca799e閱讀 1,213評(píng)論 0 0
  • 本節(jié)要介紹的是Python里面常用的幾種數(shù)據(jù)結(jié)構(gòu)。通常情況下,聲明一個(gè)變量只保存一個(gè)值是遠(yuǎn)遠(yuǎn)不夠的,我們需要將一組...
    小黑y99閱讀 65,590評(píng)論 0 9
  • 最近博客換地址了,該功能已經(jīng)移除。。。 請(qǐng)注意我博客主頁(yè)右上角的HighTime功能,昨天在看小伙伴的博客的時(shí)候發(fā)...
    MingZhe閱讀 330評(píng)論 0 2
  • 2017.11.3 星期一 晴
    暖與希望閱讀 123評(píng)論 0 0
  • 現(xiàn)在的輿論能把大眾帶到一個(gè)它需要的導(dǎo)向,因?yàn)榇蟛糠值娜硕疾辉敢馊ド罹枯浾摫澈蟮恼嫦?,而是愿意跟風(fēng)選擇別人推理出來(lái)的...
    璐啦啦Lula閱讀 118評(píng)論 0 0

友情鏈接更多精彩內(nèi)容