1. 程式人生 > >python字串和文字處理

python字串和文字處理

2.1 使用多個界定符分割字串

問題
你需要將一個字串分割為多個欄位,但是分隔符(還有周圍的空格)並不是固定的。

解決方案
string 物件的 split() 方法只適應於非常簡單的字串分割情形, 它並不允許有多個分隔符或者是分隔符周圍不確定的空格。 當你需要更加靈活的切割字串的時候,最好使用 re.split() 方法:
在這裡插入圖片描述
討論
函式 re.split() 是非常實用的,因為它允許你為分隔符指定多個正則模式。 比如,在上面的例子中,分隔符可以是逗號,分號或者是空格,並且後面緊跟著任意個的空格。 只要這個模式被找到,那麼匹配的分隔符兩邊的實體都會被當成是結果中的元素返回。 返回結果為一個欄位列表,這個跟 str.split() 返回值型別是一樣的。

當你使用 re.split() 函式時候,需要特別注意的是正則表示式中是否包含一個括號捕獲分組。 如果使用了捕獲分組,那麼被匹配的文字也將出現在結果列表中。比如,觀察一下這段程式碼執行後的結果:
在這裡插入圖片描述
獲取分割字元在某些情況下也是有用的。 比如,你可能想保留分割字串,用來在後面重新構造一個新的輸出字串:
在這裡插入圖片描述
如果你不想保留分割字串到結果列表中去,但仍然需要使用到括號來分組正則表示式的話, 確保你的分組是非捕獲分組,形如 (?:…) 。比如:
在這裡插入圖片描述

2.2 字串開頭或結尾匹配

問題
你需要通過指定的文字模式去檢查字串的開頭或者結尾,比如檔名字尾,URL Scheme等等。

解決方案
檢查字串開頭或結尾的一個簡單方法是使用 str.startswith() 或者是 str.endswith() 方法。比如:
在這裡插入圖片描述


如果你想檢查多種匹配可能,只需要將所有的匹配項放入到一個元組中去, 然後傳給 startswith() 或者 endswith() 方法:
在這裡插入圖片描述
下面是另一個例子:
在這裡插入圖片描述
奇怪的是,這個方法中必須要輸入一個元組作為引數。 如果你恰巧有一個 list 或者 set 型別的選擇項, 要確保傳遞引數前先呼叫 tuple() 將其轉換為元組型別。比如:
在這裡插入圖片描述
討論
startswith() 和 endswith() 方法提供了一個非常方便的方式去做字串開頭和結尾的檢查。 類似的操作也可以使用切片來實現,但是程式碼看起來沒有那麼優雅。比如:
在這裡插入圖片描述
你可以能還想使用正則表示式去實現,比如:
在這裡插入圖片描述
這種方式也行得通,但是對於簡單的匹配實在是有點小材大用了,本節中的方法更加簡單並且執行會更快些。

最後提一下,當和其他操作比如普通資料聚合相結合的時候 startswith() 和 endswith() 方法是很不錯的。 比如,下面這個語句檢查某個資料夾中是否存在指定的檔案型別:
在這裡插入圖片描述

2.3 用Shell萬用字元匹配字串

問題
你想使用 Unix Shell 中常用的萬用字元(比如 .py , Dat[0-9].csv 等)去匹配文字字串

解決方案
fnmatch 模組提供了兩個函式—— fnmatch() 和 fnmatchcase() ,可以用來實現這樣的匹配。用法如下:
在這裡插入圖片描述
fnmatch() 函式使用底層作業系統的大小寫敏感規則(不同的系統是不一樣的)來匹配模式。比如:
在這裡插入圖片描述
如果你對這個區別很在意,可以使用 fnmatchcase() 來代替。它完全使用你的模式大小寫匹配。比如:
在這裡插入圖片描述
這兩個函式通常會被忽略的一個特性是在處理非檔名的字串時候它們也是很有用的。 比如,假設你有一個街道地址的列表資料:
在這裡插入圖片描述

你可以像這樣寫列表推導:
在這裡插入圖片描述
討論
fnmatch() 函式匹配能力介於簡單的字串方法和強大的正則表示式之間。 如果在資料處理操作中只需要簡單的萬用字元就能完成的時候,這通常是一個比較合理的方案。

如果你的程式碼需要做檔名的匹配,最好使用 glob 模組。參考5.13小節。

2.4 字串匹配和搜尋

問題
你想匹配或者搜尋特定模式的文字

解決方案
如果你想匹配的是字面字串,那麼你通常只需要呼叫基本字串方法就行, 比如 str.find() , str.endswith() , str.startswith() 或者類似的方法:
在這裡插入圖片描述
對於複雜的匹配需要使用正則表示式和 re 模組。 為了解釋正則表示式的基本原理,假設你想匹配數字格式的日期字串比如 11/27/2012 ,你可以這樣做:
在這裡插入圖片描述
如果你想使用同一個模式去做多次匹配,你應該先將模式字串預編譯為模式物件。比如:
在這裡插入圖片描述
match() 總是從字串開始去匹配,如果你想查詢字串任意部分的模式出現位置, 使用 findall() 方法去代替。比如:
在這裡插入圖片描述
在定義正則式的時候,通常會利用括號去捕獲分組。比如:
在這裡插入圖片描述
捕獲分組可以使得後面的處理更加簡單,因為可以分別將每個組的內容提取出來。比如:
在這裡插入圖片描述
findall() 方法會搜尋文字並以列表形式返回所有的匹配。 如果你想以迭代方式返回匹配,可以使用 finditer() 方法來代替,比如:
在這裡插入圖片描述
討論
關於正則表示式理論的教程已經超出了本書的範圍。 不過,這一節闡述了使用re模組進行匹配和搜尋文字的最基本方法。 核心步驟就是先使用 re.compile() 編譯正則表示式字串, 然後使用 match() , findall() 或者 finditer() 等方法。

當寫正則式字串的時候,相對普遍的做法是使用原始字串比如 r’(\d+)/(\d+)/(\d+)’ 。 這種字串將不去解析反斜槓,這在正則表示式中是很有用的。 如果不這樣做的話,你必須使用兩個反斜槓,類似 ‘(\d+)/(\d+)/(\d+)’ 。

需要注意的是 match() 方法僅僅檢查字串的開始部分。它的匹配結果有可能並不是你期望的那樣。比如:
在這裡插入圖片描述
如果你想精確匹配,確保你的正則表示式以$結尾,就像這麼這樣:
在這裡插入圖片描述
最後,如果你僅僅是做一次簡單的文字匹配/搜尋操作的話,可以略過編譯部分,直接使用 re 模組級別的函式。比如:
在這裡插入圖片描述
但是需要注意的是,如果你打算做大量的匹配和搜尋操作的話,最好先編譯正則表示式,然後再重複使用它。 模組級別的函式會將最近編譯過的模式快取起來,因此並不會消耗太多的效能, 但是如果使用預編譯模式的話,你將會減少查詢和一些額外的處理損耗。

2.5 字串搜尋和替換

問題
你想在字串中搜索和匹配指定的文字模式

解決方案
對於簡單的字面模式,直接使用 str.replace() 方法即可,比如:
在這裡插入圖片描述
對於複雜的模式,請使用 re 模組中的 sub() 函式。 為了說明這個,假設你想將形式為 11/27/2012 的日期字串改成 2012-11-27 。示例如下:
在這裡插入圖片描述
sub() 函式中的第一個引數是被匹配的模式,第二個引數是替換模式。反斜槓數字比如 \3 指向前面模式的捕獲組號。

如果你打算用相同的模式做多次替換,考慮先編譯它來提升效能。比如:
在這裡插入圖片描述
對於更加複雜的替換,可以傳遞一個替換回調函式來代替,比如:
在這裡插入圖片描述
一個替換回調函式的引數是一個 match 物件,也就是 match() 或者 find() 返回的物件。 使用 group() 方法來提取特定的匹配部分。回撥函式最後返回替換字串。

如果除了替換後的結果外,你還想知道有多少替換髮生了,可以使用 re.subn() 來代替。比如:
在這裡插入圖片描述
討論
關於正則表示式搜尋和替換,上面演示的 sub() 方法基本已經涵蓋了所有。 其實最難的部分就是編寫正則表示式模式,這個最好是留給讀者自己去練習了。

2.6 字串忽略大小寫的搜尋替換

問題
你需要以忽略大小寫的方式搜尋與替換文字字串

解決方案
為了在文字操作時忽略大小寫,你需要在使用 re 模組的時候給這些操作提供 re.IGNORECASE 標誌引數。比如:
在這裡插入圖片描述
最後的那個例子揭示了一個小缺陷,替換字串並不會自動跟被匹配字串的大小寫保持一致。 為了修復這個,你可能需要一個輔助函式,就像下面的這樣:

在這裡插入圖片描述
下面是使用上述函式的方法:
在這裡插入圖片描述
譯者注: matchcase(‘snake’) 返回了一個回撥函式(引數必須是 match 物件),前面一節提到過, sub() 函式除了接受替換字串外,還能接受一個回撥函式。

討論
對於一般的忽略大小寫的匹配操作,簡單的傳遞一個 re.IGNORECASE 標誌引數就已經足夠了。 但是需要注意的是,這個對於某些需要大小寫轉換的Unicode匹配可能還不夠, 參考2.10小節瞭解更多細節。

2.7 最短匹配模式

問題
你正在試著用正則表示式匹配某個文字模式,但是它找到的是模式的最長可能匹配。 而你想修改它變成查詢最短的可能匹配。

解決方案
這個問題一般出現在需要匹配一對分隔符之間的文字的時候(比如引號包含的字串)。 為了說明清楚,考慮如下的例子:在這裡插入圖片描述
在這個例子中,模式 r’"(.)"’ 的意圖是匹配被雙引號包含的文字。 但是在正則表示式中操作符是貪婪的,因此匹配操作會查詢最長的可能匹配。 於是在第二個例子中搜索 text2 的時候返回結果並不是我們想要的。

為了修正這個問題,可以在模式中的*操作符後面加上?修飾符,就像這樣:
在這裡插入圖片描述
這樣就使得匹配變成非貪婪模式,從而得到最短的匹配,也就是我們想要的結果。
討論
這一節展示了在寫包含點(.)字元的正則表示式的時候遇到的一些常見問題。 在一個模式字串中,點(.)匹配除了換行外的任何字元。 然而,如果你將點(.)號放在開始與結束符(比如引號)之間的時候,那麼匹配操作會查詢符合模式的最長可能匹配。 這樣通常會導致很多中間的被開始與結束符包含的文字被忽略掉,並最終被包含在匹配結果字串中返回。 通過在 * 或者 + 這樣的操作符後面新增一個 ? 可以強制匹配演算法改成尋找最短的可能匹配

2.8 多行匹配模式

問題
你正在試著使用正則表示式去匹配一大塊的文字,而你需要跨越多行去匹配。

解決方案
這個問題很典型的出現在當你用點(.)去匹配任意字元的時候,忘記了點(.)不能匹配換行符的事實。 比如,假設你想試著去匹配C語言分割的註釋:在這裡插入圖片描述
為了修正這個問題,你可以修改模式字串,增加對換行的支援。比如:
在這裡插入圖片描述
在這個模式中, (?:.|\n) 指定了一個非捕獲組 (也就是它定義了一個僅僅用來做匹配,而不能通過單獨捕獲或者編號的組)。
討論
re.compile() 函式接受一個標誌引數叫 re.DOTALL ,在這裡非常有用。 它可以讓正則表示式中的點(.)匹配包括換行符在內的任意字元。比如:
在這裡插入圖片描述
對於簡單的情況使用 re.DOTALL 標記引數工作的很好, 但是如果模式非常複雜或者是為了構造字串令牌而將多個模式合併起來(2.18節有詳細描述), 這時候使用這個標記引數就可能出現一些問題。 如果讓你選擇的話,最好還是定義自己的正則表示式模式,這樣它可以在不需要額外的標記引數下也能工作的很好。

2.9 將Unicode文字標準化

問題
你正在處理Unicode字串,需要確保所有字串在底層有相同的表示。

解決方案
在Unicode中,某些字元能夠用多個合法的編碼表示。為了說明,考慮下面的這個例子:
在這裡插入圖片描述
這裡的文字”Spicy Jalapeño”使用了兩種形式來表示。 第一種使用整體字元”ñ”(U+00F1),第二種使用拉丁字母”n”後面跟一個”~”的組合字元(U+0303)。

在需要比較字串的程式中使用字元的多種表示會產生問題。 為了修正這個問題,你可以使用unicodedata模組先將文字標準化:
在這裡插入圖片描述
normalize() 第一個引數指定字串標準化的方式。 NFC表示字元應該是整體組成(比如可能的話就使用單一編碼),而NFD表示字元應該分解為多個組合字元表示。

Python同樣支援擴充套件的標準化形式NFKC和NFKD,它們在處理某些字元的時候增加了額外的相容特性。比如:
在這裡插入圖片描述
討論
標準化對於任何需要以一致的方式處理Unicode文字的程式都是非常重要的。 當處理來自使用者輸入的字串而你很難去控制編碼的時候尤其如此。

在清理和過濾文字的時候字元的標準化也是很重要的。 比如,假設你想清除掉一些文字上面的變音符的時候(可能是為了搜尋和匹配):
在這裡插入圖片描述
最後一個例子展示了 unicodedata 模組的另一個重要方面,也就是測試字元類的工具函式。 combining() 函式可以測試一個字元是否為和音字元。 在這個模組中還有其他函式用於查詢字元類別,測試是否為數字字元等等。

Unicode顯然是一個很大的主題。如果想更深入的瞭解關於標準化方面的資訊, 請看考 Unicode官網中關於這部分的說明 Ned Batchelder在 他的網站 上對Python的Unicode處理問題也有一個很好的介紹。

2.10 在正則式中使用Unicode

問題
你正在使用正則表示式處理文字,但是關注的是Unicode字元處理。

解決方案
預設情況下 re 模組已經對一些Unicode字元類有了基本的支援。 比如, \d 已經匹配任意的unicode數字字元了:
在這裡插入圖片描述
如果你想在模式中包含指定的Unicode字元,你可以使用Unicode字元對應的轉義序列(比如 \uFFF 或者 \UFFFFFFF )。 比如,下面是一個匹配幾個不同阿拉伯編碼頁面中所有字元的正則表示式:
在這裡插入圖片描述
當執行匹配和搜尋操作的時候,最好是先標準化並且清理所有文字為標準化格式(參考2.9小節)。 但是同樣也應該注意一些特殊情況,比如在忽略大小寫匹配和大小寫轉換時的行為。
在這裡插入圖片描述
討論
混合使用Unicode和正則表示式通常會讓你抓狂。 如果你真的打算這樣做的話,最好考慮下安裝第三方正則式庫, 它們會為Unicode的大小寫轉換和其他大量有趣特性提供全面的支援,包括模糊匹配。

2.11 刪除字串中不需要的字元

問題
你想去掉文字字串開頭,結尾或者中間不想要的字元,比如空白。

解決方案
strip() 方法能用於刪除開始或結尾的字元。 lstrip() 和 rstrip() 分別從左和從右執行刪除操作。 預設情況下,這些方法會去除空白字元,但是你也可以指定其他字元。比如:
在這裡插入圖片描述
討論
這些 strip() 方法在讀取和清理資料以備後續處理的時候是經常會被用到的。 比如,你可以用它們來去掉空格,引號和完成其他任務。

但是需要注意的是去除操作不會對字串的中間的文字產生任何影響。比如:
在這裡插入圖片描述
如果你想處理中間的空格,那麼你需要求助其他技術。比如使用 replace() 方法或者是用正則表示式替換。示例如下:
在這裡插入圖片描述
通常情況下你想將字串 strip 操作和其他迭代操作相結合,比如從檔案中讀取多行資料。 如果是這樣的話,那麼生成器表示式就可以大顯身手了。比如:
在這裡插入圖片描述
在這裡,表示式 lines = (line.strip() for line in f) 執行資料轉換操作。 這種方式非常高效,因為它不需要預先讀取所有資料放到一個臨時的列表中去。 它僅僅只是建立一個生成器,並且每次返回行之前會先執行 strip 操作。

對於更高階的strip,你可能需要使用 translate() 方法。請參閱下一節瞭解更多關於字串清理的內容。

2.12 審查清理文字字串

問題
一些無聊的幼稚黑客在你的網站頁面表單中輸入文字”pýtĥöñ”,然後你想將這些字元清理掉。

解決方案
文字清理問題會涉及到包括文字解析與資料處理等一系列問題。 在非常簡單的情形下,你可能會選擇使用字串函式(比如 str.upper() 和 str.lower() )將文字轉為標準格式。 使用 str.replace() 或者 re.sub() 的簡單替換操作能刪除或者改變指定的字元序列。 你同樣還可以使用2.9小節的 unicodedata.normalize() 函式將unicode文字標準化。

然後,有時候你可能還想在清理操作上更進一步。比如,你可能想消除整個區間上的字元或者去除變音符。 為了這樣做,你可以使用經常會被忽視的 str.translate() 方法。 為了演示,假設你現在有下面這個凌亂的字串:
在這裡插入圖片描述
第一步是清理空白字元。為了這樣做,先建立一個小的轉換表格然後使用 translate() 方法:
在這裡插入圖片描述
正如你看的那樣,空白字元 \t 和 \f 已經被重新對映到一個空格。回車字元r直接被刪除。

你可以以這個表格為基礎進一步構建更大的表格。比如,讓我們刪除所有的和音符:
在這裡插入圖片描述
上面例子中,通過使用 dict.fromkeys() 方法構造一個字典,每個Unicode和音符作為鍵,對應的值全部為 None 。

然後使用 unicodedata.normalize() 將原始輸入標準化為分解形式字元。 然後再呼叫 translate 函式刪除所有重音符。 同樣的技術也可以被用來刪除其他型別的字元(比如控制字元等)。

作為另一個例子,這裡構造一個將所有Unicode數字字元對映到對應的ASCII字元上的表格:
在這裡插入圖片描述
另一種清理文字的技術涉及到I/O解碼與編碼函式。這裡的思路是先對文字做一些初步的清理, 然後再結合 encode() 或者 decode() 操作來清除或修改它。比如:
在這裡插入圖片描述
這裡的標準化操作將原來的文字分解為單獨的和音符。接下來的ASCII編碼/解碼只是簡單的一下子丟棄掉那些字元。 當然,這種方法僅僅只在最後的目標就是獲取到文字對應ACSII表示的時候生效。

討論
文字字元清理一個最主要的問題應該是執行的效能。一般來講,程式碼越簡單執行越快。 對於簡單的替換操作, str.replace() 方法通常是最快的,甚至在你需要多次呼叫的時候。 比如,為了清理空白字元,你可以這樣做:
在這裡插入圖片描述
如果你去測試的話,你就會發現這種方式會比使用 translate() 或者正則表示式要快很多。

另一方面,如果你需要執行任何複雜字元對字元的重新對映或者刪除操作的話, tanslate() 方法會非常的快。

從大的方面來講,對於你的應用程式來說效能是你不得不去自己研究的東西。 不幸的是,我們不可能給你建議一個特定的技術,使它能夠適應所有的情況。 因此實際情況中需要你自己去嘗試不同的方法並評估它。

儘管這一節集中討論的是文字,但是類似的技術也可以適用於位元組,包括簡單的替換,轉換和正則表示式。

2.13 字串對齊

問題
你想通過某種對齊方式來格式化字串

解決方案
對於基本的字串對齊操作,可以使用字串的 ljust() , rjust() 和 center() 方法。比如:
在這裡插入圖片描述
所有這些方法都能接受一個可選的填充字元。比如:
在這裡插入圖片描述
函式 format() 同樣可以用來很容易的對齊字串。 你要做的就是使用 <,> 或者 ^ 字元後面緊跟一個指定的寬度。比如:
在這裡插入圖片描述
如果你想指定一個非空格的填充字元,將它寫到對齊字元的前面即可:
在這裡插入圖片描述
當格式化多個值的時候,這些格式程式碼也可以被用在 format() 方法中。比如:
在這裡插入圖片描述
format() 函式的一個好處是它不僅適用於字串。它可以用來格式化任何值,使得它非常的通用。 比如,你可以用它來格式化數字:
在這裡插入圖片描述
討論
在老的程式碼中,你經常會看到被用來格式化文字的 % 操作符。比如:
在這裡插入圖片描述
但是,在新版本程式碼中,你應該優先選擇 format() 函式或者方法。 format() 要比 % 操作符的功能更為強大。 並且 format() 也比使用 ljust() , rjust() 或 center() 方法更通用, 因為它可以用來格式化任意物件,而不僅僅是字串。

如果想要完全瞭解 format() 函式的有用特性, 請參考 線上Python文件

2.14 合併拼接字串

問題
你想將幾個小的字串合併為一個大的字串

解決方案
如果你想要合併的字串是在一個序列或者 iterable 中,那麼最快的方式就是使用 join() 方法。比如:
在這裡插入圖片描述
初看起來,這種語法看上去會比較怪,但是 join() 被指定為字串的一個方法。 這樣做的部分原因是你想去連線的物件可能來自各種不同的資料序列(比如列表,元組,字典,檔案,集合或生成器等), 如果在所有這些物件上都定義一個 join() 方法明顯是冗餘的。 因此你只需要指定你想要的分割字串並呼叫他的 join() 方法去將文字片段組合起來。

如果你僅僅只是合併少數幾個字串,使用加號(+)通常已經足夠了:
在這裡插入圖片描述
加號(+)操作符在作為一些複雜字串格式化的替代方案的時候通常也工作的很好,比如:
在這裡插入圖片描述
如果你想在原始碼中將兩個字面字串合併起來,你只需要簡單的將它們放到一起,不需要用加號(+)。比如:
在這裡插入圖片描述
討論
字串合併可能看上去並不需要用一整節來討論。 但是不應該小看這個問題,程式設計師通常在字串格式化的時候因為選擇不當而給應用程式帶來嚴重效能損失。

最重要的需要引起注意的是,當我們使用加號(+)操作符去連線大量的字串的時候是非常低效率的, 因為加號連線會引起記憶體複製以及垃圾回收操作。 特別的,你永遠都不應像下面這樣寫字串連線程式碼:
在這裡插入圖片描述
這種寫法會比使用 join() 方法執行的要慢一些,因為每一次執行+=操作的時候會建立一個新的字串物件。 你最好是先收集所有的字串片段然後再將它們連線起來。

一個相對比較聰明的技巧是利用生成器表示式(參考1.19小節)轉換資料為字串的同時合併字串,比如:
在這裡插入圖片描述
同樣還得注意不必要的字串連線操作。有時候程式設計師在沒有必要做連線操作的時候仍然多此一舉。比如在列印的時候:
在這裡插入圖片描述
當混合使用I/O操作和字串連線操作的時候,有時候需要仔細研究你的程式。 比如,考慮下面的兩端程式碼片段:
在這裡插入圖片描述
如果兩個字串很小,那麼第一個版本效能會更好些,因為I/O系統呼叫天生就慢。 另外一方面,如果兩個字串很大,那麼第二個版本可能會更加高效, 因為它避免了建立一個很大的臨時結果並且要複製大量的記憶體塊資料。 還是那句話,有時候是需要根據你的應用程式特點來決定應該使用哪種方案。

最後談一下,如果你準備編寫構建大量小字串的輸出程式碼, 你最好考慮下使用生成器函式,利用yield語句產生輸出片段。比如:
在這裡插入圖片描述
這種方法一個有趣的方面是它並沒有對輸出片段到底要怎樣組織做出假設。 例如,你可以簡單的使用 join() 方法將這些片段合併起來:
在這裡插入圖片描述
或者你也可以將字串片段重定向到I/O:
在這裡插入圖片描述
再或者你還可以寫出一些結合I/O操作的混合方案:
在這裡插入圖片描述
這裡的關鍵點在於原始的生成器函式並不需要知道使用細節,它只負責生成字串片段就行了。

2.15 字串中插入變數

問題
你想建立一個內嵌變數的字串,變數被它的值所表示的字串替換掉。

解決方案
Python並沒有對在字串中簡單替換變數值提供直接的支援。 但是通過使用字串的 format() 方法來解決這個問題。比如:
在這裡插入圖片描述
或者,如果要被替換的變數能在變數域中找到, 那麼你可以結合使用 format_map() 和 vars() 。就像下面這樣:
在這裡插入圖片描述
vars() 還有一個有意思的特性就是它也適用於物件例項。比如:
在這裡插入圖片描述
format 和 format_map() 的一個缺陷就是它們並不能很好的處理變數缺失的情況,比如:
在這裡插入圖片描述
一種避免這種錯誤的方法是另外定義一個含有 missing() 方法的字典物件,就像下面這樣:
在這裡插入圖片描述
現在你可以利用這個類包裝輸入後傳遞給 format_map() :
在這裡插入圖片描述
如果你發現自己在程式碼中頻繁的執行這些步驟,你可以將變數替換步驟用一個工具函式封裝起來。就像下面這樣:
在這裡插入圖片描述
現在你可以像下面這樣寫了:
在這裡插入圖片描述
討論
多年以來由於Python缺乏對變數替換的內建支援而導致了各種不同的解決方案。 作為本節中展示的一個可能的解決方案,你可以有時候會看到像下面這樣的字串格式化程式碼:
在這裡插入圖片描述
你可能還會看到字串模板的使用:
在這裡插入圖片描述
然而, format() 和 format_map() 相比較上面這些方案而已更加先進,因此應該被優先選擇。 使用 format() 方法還有一個好處就是你可以獲得對字串格式化的所有支援(對齊,填充,數字格式化等待), 而這些特性是使用像模板字串之類的方案不可能獲得的。

本機還部分介紹了一些高階特性。對映或者字典類中鮮為人知的 missing() 方法可以讓你定義如何處理缺失的值。 在 SafeSub 類中,這個方法被定義為對缺失的值返回一個佔位符。 你可以發現缺失的值會出現在結果字串中(在除錯的時候可能很有用),而不是產生一個 KeyError 異常。

sub() 函式使用 sys._getframe(1) 返回呼叫者的棧幀。可以從中訪問屬性 f_locals 來獲得區域性變數。 毫無疑問絕大部分情況下在程式碼中去直接操作棧幀應該是不推薦的。 但是,對於像字串替換工具函式而言它是非常有用的。 另外,值得注意的是 f_locals 是一個複製呼叫函式的本地變數的字典。 儘管你可以改變 f_locals 的內容,但是這個修改對於後面的變數訪問沒有任何影響。 所以,雖說訪問一個棧幀看上去很邪惡,但是對它的任何操作不會覆蓋和改變呼叫者本地變數的值。

2.16 以指定列寬格式化字串
問題
你有一些長字串,想以指定的列寬將它們重新格式化。

解決方案
使用 textwrap 模組來格式化字串的輸出。比如,假如你有下列的長字串:

s = “Look into my eyes, look into my eyes, the eyes, the eyes,
the eyes, not around the eyes, don’t look around the eyes,
look into my eyes, you’re under.”

下面演示使用 textwrap 格式化字串的多種方式:
在這裡插入圖片描述

討論
textwrap 模組對於字串列印是非常有用的,特別是當你希望輸出自動匹配終端大小的時候。 你可以使用 os.get_terminal_size() 方法來獲取終端的大小尺寸。比如:
在這裡插入圖片描述
但是我自己用python3.6測試的時候這段程式碼竟然不能執行,出現瞭如下錯誤
在這裡插入圖片描述

2.17 在字串中處理html和xml

問題
你想將HTML或者XML實體如 &entity; 或 &#code; 替換為對應的文字。 再者,你需要轉換文字中特定的字元(比如<, >, 或 &)。

解決方案
如果你想替換文字字串中的 ‘<’ 或者 ‘>’ ,使用 html.escape() 函式可以很容易的完成。比如:
在這裡插入圖片描述
如果你正在處理的是ASCII文字,並且想將非ASCII文字對應的編碼實體嵌入進去, 可以給某些I/O函式傳遞引數 errors=‘xmlcharrefreplace’ 來達到這個目。比如:
在這裡插入圖片描述
為了替換文字中的編碼實體,你需要使用另外一種方法。 如果你正在處理HTML或者XML文字,試著先使用一個合適的HTML或者XML解析器。 通常情況下,這些工具會自動替換這些編碼值,你無需擔心。

有時候,如果你接收到了一些含有編碼值的原始文字,需要手動去做替換, 通常你只需要使用HTML或者XML解析器的一些相關工具函式/方法即可。比如:
在這裡插入圖片描述
討論
在生成HTML或者XML文字的時候,如果正確的轉換特殊標記字元是一個很容易被忽視的細節。 特別是當你使用 print() 函式或者其他字串格式化來產生輸出的時候。 使用像 html.escape() 的工具函式可以很容易的解決這類問題。

如果你想以其他方式處理文字,還有一些其他的工具函式比如 xml.sax.saxutils.unescapge() 可以幫助你。 然而,你應該先調研清楚怎樣使用一個合適的解析器。 比如,如果你在處理HTML或XML文字, 使用某個解析模組比如 html.parse 或 xml.etree.ElementTree 已經幫你自動處理了相關的替換細節。