python 3.3.3 字面量,正則,反斜杠和原始字符串


兩個不起眼但是比較重要的設定

  • Python str類型的字面量解釋器

當反斜杠及其緊接字符無法構成一個具有特殊含義的序列('recognized escape sequences')時,Python選擇保留全部字符.直接看例子:

>>> '\c'
'\\c'
>>> '\d'
'\\d'

官方管'\c'這種序列叫'unrecognized escape sequences'.官方文檔相應部分:

Unlike Standard C, all unrecognized escape sequences are left in the string unchanged, i.e., the backslash is left in the string. (This behavior is useful when debugging: if an escape sequence is mistyped, the resulting output is more easily recognized as broken.) 

按這段英文的意思,估計C語言里面,'c'和'\c'是等同的.Python是'\\c'和'\c'等同.這個等以后學C語言再確定.

與上面對應的是,如果緊接字符能夠和反斜杠構成'recognized escape sequences'的全部或者起始部分,中文就叫'被承認的轉義序列'吧.比如:

>>> '\b'
'\x08'
>>> '\n'
'\n'
>>> '\x'
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-1: truncated \xXX escape
>>> '\N'
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-1: malformed \N character escape
>>> '\U'
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-1: truncated \UXXXXXXXX escape
>>> '\u'
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-1: truncated \uXXXX escape
  • Python re模塊正則表達式解釋器

當反斜杠及其緊接字符無法構成一個具有特殊含義的序列(special sequences)時,re選擇忽略反斜杠,例如:

>>> re.findall('\e','eee')
['e', 'e', 'e']
>>> re.findall('e','eee')
['e', 'e', 'e']

可見,'\e'和'e'起到了完全一樣的效果.Python相關文檔描述是:

If the ordinary character is not on the list, then the resulting RE will match the second character. For example, \$ matches the character '$'.

與上面對應的是,如果能夠構成special sequences,那么re會解釋為相應含義.例如:

>>> re.findall('\w','abcdefghijklmnopqrstuvwxyz')
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

 

 

字面量

 

字面量(Literals),是用於表示一些Python內建類型的常量的符號.最常見的字面量類型是str literals 和 bytes literals.

比如:

>>> 'abc'
'abc'
>>> "abc"
'abc'
>>> '啊哦額'
'啊哦額'
>>> b'abc'
b'abc'
>>> r'\n'
'\\n'
>>> b'啊哦額'
SyntaxError: bytes can only contain ASCII literal characters.

反斜杠\的用途按緊接其后的字符種類可划分為3類:

1.將特殊字符轉換為字面量.這特殊字符包括(單引號,雙引號,反斜杠):'"\

2.將普通字符轉換為特殊序列.包括:abfNnrtuUvx0123456789.

(注意,bytes字面量中,NuU這三個普通字符無法被轉義成特殊序列)

3.將"新行"和自身忽略掉.這個比較抽象,舉例說明:py文件中,某個字符串太長了,以至於需要分兩行寫,那么你可以插個反斜杠,緊接着換行,然后寫剩余字符串.

 

下面是官方文檔歸納的表:

 

Escape Sequence Meaning Notes
\newline Backslash and newline ignored  
\\ Backslash (\)  
\' Single quote (')  
\" Double quote (")  
\a ASCII Bell (BEL)  
\b ASCII Backspace (BS)  
\f ASCII Formfeed (FF)  
\n ASCII Linefeed (LF)  
\r ASCII Carriage Return (CR)  
\t ASCII Horizontal Tab (TAB)  
\v ASCII Vertical Tab (VT)  
\ooo Character with octal value ooo (1,3)
\xhh Character with hex value hh (2,3)

Escape sequences only recognized in string literals are:

Escape Sequence Meaning Notes
\N{name} Character named name in the Unicode database (4)
\uxxxx Character with 16-bit hex value xxxx (5)
\Uxxxxxxxx Character with 32-bit hex value xxxxxxxx (6)

舉例:

>>> '\N{END OF LINE}'
'\n'
>>> '\N{HORIZONTAL TABULATION}'
'\t'
>>> '\u9f6a'==''
True
>>> '\1'=='\01'
True
>>> '\1'=='\001'
True
>>> '\1'=='\0000001'
False

 

正則

 

  • 正則表達式的反斜杠的作用

一種是使緊跟在后面的元字符(special characters或metacharacters)失去特殊含義,變為字面量.這些元字符有14個:

.^$*+?{}[]()\|

另一種是使緊跟在后面的普通字符變得具有特殊含義.這些普通字符是:

AbBdDsSwWZ0123456789

以及在str字面量中能被反斜杠轉義的字符:

\'"abfnrtuUvx0123456789

例如:

>>> re.findall('\"','"')
['"']

正則pattern的反斜杠的作用和Python字面量的反斜杠類似,這據說是帶來"反斜杠災難"的根源.最典型的莫過於你需要用正則'\\\\'才能匹配字面量反斜杠'\\'.

為方便說明,我們假設re.search(pattern,string)中,pattern表示正則表達式字符串,string表示待匹配的字符串.

>>> re.search('\\\\','\\')
<_sre.SRE_Match object at 0x02858528>

詳細來說就是一個文本層級的反斜杠'\'(比如你在txt文件中看到的反斜杠),對應Python str 字面量的'\\',對應正則pattern的'\\\\'.這個確實比較難以理解,實在不行就住這點就好:如果不是最簡單的正則類型(比如'ab'),強烈推薦對pattern使用r前綴符.這樣容易理解:

>>> re.search(r'\\','\\')
<_sre.SRE_Match object at 0x02858448>

 

注意:

  • 1.多重含義的特殊序列處理機制

b0123456789比較特殊,它們在Python字面量和re正則中都能和反斜杠構成作用不同的特殊序列.例如\b,在python 字面量中解釋為"退格鍵".re正則中解釋為'單詞邊界'.python 字面量有優先解釋權,如下可證:

>>> re.findall('\b','\b')  #'\b'被優先解釋為退格鍵,而不是單詞邊界
['\x08']
>>> re.findall('\\b','\b')
[]
>>> re.findall('\\b','b') 
['', '']

再比如:

>>> re.findall('(a)\1\1','aaa') #\1按字面量優先解釋為八進制字符串,因此無匹配結果
[]
>>> re.findall('(a)\\1\\1','aaa')  #\\1按正則引擎層級的反斜杠解釋為第一個匹配組提取到的字符,相當於'(a)aa'
['a']
>>> re.findall('a\1\1','a\1\1') #\1按字面量優先解釋為八進制字符串,所以有匹配結果
['a\x01\x01']

了解這個設置有什么用?

1.當你想使用正則層級的特殊序列\1時,如果你沒有使用r作為前綴,那么你必須使用\\1才能如願.

2.當你想使用字面量層級的特殊序列\1時,則不能使用r作為pattern前綴.

想想,你有可能在一個r前綴的字符串中寫出能夠匹配值為1的八進制字符串的pattern嗎?

也許我太較真了,因為實踐中好像從沒遇到過需要匹配值為1的八進制字符串的情況,但理論上就是這樣的.

 

  • 2.正則表達式中特殊序列的准確定義的猜想

官方文檔下面的一句話值得推敲:

Note that \b is used to represent word boundaries, and means “backspace” only inside character classes

意思是說\b只有在[...]里面時才表示退格鍵,這顯然是錯的.比如下面這個例子,\b沒有在[]之內,但它是按"退格鍵"解釋的,並非"單詞邊界":

>>> re.findall('\b','\b')
['\x08']

除非官方文檔描述的\b是指文本層面的數據(比如你在txt文檔里看到的\b).

由此引出了一個猜想,re的正則pattern中"反斜杠+普通字符"構成特殊序列或"反斜杠+特殊字符"構成字面量--這種描述中的反斜杠准確來說是指兩個反斜杠!

仍然是舉例說明:

>>> re.findall('\\b\w+\\b','one two three')  #必須用\\b才能表示單詞邊界
['one', 'two', 'three']
>>> re.findall('\\b\\w+\\b','one two three')  #想想,為什么\w和\\w都一樣
['one', 'two', 'three']
>>> re.findall('\d','123')
['1', '2', '3']
>>> re.findall('\\d','123')
['1', '2', '3']

 

 

  • 3.u和U只在str字面量中才能被轉義,bytes字面量中是普通字符.

 

以下是我猜測的正則表達式分析器和Python字面量分析器的傳遞規則表格:

Python string literal values passed to regular expression number of characters what regular expression engine does real meaning for regular expression
\e \e 2 ignore the backslash e
\\e \e 2 ignore the backslash e
e e 1 nothing spacial e
\n \n 1 nothing spacial 換行符
\\n \n 2 \n is special 換行符
\b \b 1 nothing spacial 退格鍵
\\b \b 2 \b is special word boundary
\s \s 2 \s is special Unicode whitespace characters
\\ \ 1 must followed by a charcter Can't form any meaning
\\\\ \\ 2 remove all special meanning of \ \
* * 1 * is special repeat the left characters 0 or more times
\* \* 2 remove all special meanning of * *

 

最后是待探究的例子:

>>> re.findall('\n','\n\n')
['\n', '\n']
>>> re.findall('\\n','\n\n')
['\n', '\n']
>>> re.findall('\\\n','\n\n')
['\n', '\n']
>>> re.findall('\\\\n','\n\n')
[]
>>> re.findall('\b','\b\b')
['\x08', '\x08']
>>> re.findall('\\b','\b\b')
[]
>>> re.findall('\\\b','\b\b')
['\x08', '\x08']
>>> re.findall('\\\\b','\b\b')
[]
>>> re.findall('\c','\c\c')
['c', 'c']
>>> re.findall('\\c','\c\c')
['c', 'c']
>>> re.findall('\\\c','\c\c')
['\\c', '\\c']
>>> re.findall('\\\\c','\c\c')
['\\c', '\\c'

參考:

Python 3.3.3 官方文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM