这是前几天偶尔碰到的问题, 发现跟本身的对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的功能了.