Python r”\" SyntaxError 问题分析

这是前几天偶尔碰到的问题, 发现跟自己的对python理解不一致, 于是网上搜了下, 说是Python的Bug(不过文档上是有说过说不支持的) 正好最近在看python源码, 于是就通过源码分析了下.

1. 首先,说下问题:

""在python字符串中算特殊字符, 起转义作用, \n转义n变回车, \转义\等等,这个应该没啥问题. 那么正常情况下a="" 是存在语法问题, 因为\转义了右边的",所以字符串少一个右括号,错误原因很明显了. 但是python(很作死的)有个r操作符(全称 原始字符串操作符), 就是对字符串内的内容按字面意思解析,不做特殊处理,所以如果a=r"\n" 输出的就是"\n",而不是回车,为什么是"\", goto 1 再看看. 那么现在问题来了, 按r的功能, a=r"", 应该没问题,因为在r的场子里,\是不能转义",但是现在还是给你一个语法错误,syntaxError.

2. 然后,我想说:

其实我刚才说错了, 这不是一个语法错误, 这tmd是个词法错误.(略装B的说法,语法词法概念模糊的可以网上稍微看下). 为什么这么说, 跟了下python的源码,发现问题出在Parser\tokenizer.c内, python对字符串对象的词法解析部分.代码如下:

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

其实蛮简单的.
几个地方需要扯一下:

  • 是letter_quote:这个跳转标签,请关注之,最后揭晓.
  • tok_nextc从输入流中获取下一个字符.
  • tok_backup将字符放回输入流去.
  • triple 和tripcount这两个变量跟三引号处理有关,triple标记现在是否处于三引号模式, 等于1时,说明当前处于三引号模式, tripcount记录连续的引号数.

Ok,剩下的有点c基础的就可以啃了.
这里简单分析下, if (c == ''' || c == '"')当前字符是'或"进入字符串对象的词法解析过程了,所以python支持'和"两种引号字符。在for循环一共有4个else if,其实就是说明python的字符串有4类特殊字符。

  1. \n回车. 如果在非三引号模式下,检测到回车后,设置下相应的错误码, 然后返回失败, 相关的错误定义如下:
#define E_EOFS      23  /* EOF in triple-quoted string */  
#define E_EOLS      24  /* EOL in single-quoted string */  

这搞的好像在单引号模式下,不能输回车试的,但是有点python经验的都知道行尾加个\就可以输入回车, 在下一行重头再来.是的, 请记住, 要输入#. 从中也可以看出,在三引号模式下, 回车是可以随便输的.

  1. EOF文件尾(输入流停水了). 这个简单除暴了,根据当前模式,设置下错误码,然后返回失败。

  2. quote引号. 先跳过if (tok->cur - tok->start == quote2). 单看if (!triple || tripcount == 3). 如果没在三引号模式下,又"摸到"个quote,那当前这个字符串词法解析就完了, break出去,一个字符串就ko了(尼玛,略简单的说). 回过头说下if (tok->cur - tok->start == quote2) 这个,就是判断三引号的地方了.跟本文核心内容无关, 就不扯了哈.

  3. '\',终于到这货了. 处理也很简单, 核心就是c = tok_nextc(tok), 把的下个字符从输入流里取了,其他什么都不管. 因为还在词法阶段,没法解析转义字符.那么现在很多问题找到答案:

  • 加个\,单行模式可以输入回车了,因为python在解析到一个\后,直接把后面的回车从输入流里取出来了, 同时也说明,回车一定要紧跟\后面,因为\只取他后面的一个字符.
  • ""为什么失败, 应为解析到\后,\把"取走了.那么下面的解析时,面对的就是\n,所以Syntax EOL in single-quoted string

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.

3. 后话

其实解决这个问题还是很简单的, 加个标示变量

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的功能了.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容