# 第三章 使用字符串
> 來源:http://www.cnblogs.com/Marlowes/p/5312236.html
> 作者:Marlowes
讀者已經知道了什么是字符串,也知道如何創建它們。利用索引和分片訪問字符串中的單個字符也已經不在話下了。那么本章將會介紹如何使用字符串格式化其他的值(如打印特殊格式的字符串),并簡單了解一下利用字符串的分割、連接、搜索等方法能做些什么。
## 3.1 基本字符串操作
所有標準的序列操作(索引、分片、乘法、判斷成員資格、求長度、取最小值和最大值)對字符串同樣適用,上一章已經講述了這些操作。但是,請記住字符串都是不可變的。因此,如下所示的項或分片賦值都是不合法的:
```
>>> website = "http://www.python.org"
>>> website[-3:] = "com" Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
```
## 3.2 字符串格式化:精簡版
如果初次接觸Python編程,那么Python提供的所有字符串格式化功能可能用不到太多。因此,這里只簡單介紹一些主要的內容。如果讀者對細節感興趣,可以參見下一章,否則可以直接閱讀3.4節。
字符串格式化使用字符串格式化操作符(這個名字還是很恰當的)即百分號`%`來實現。
_注:`%`也可以用作模運算(求余)操作符。_
在`%`的左側放置一個字符串(格式化字符串),而右側則放置希望被格式化的值。可以使用一個值,如一個字符串或者數字,也可以使用多個值的元組或者下一章將會討論的字典(如果希望格式化多個值的話),這部分內容將在下一章進行討論。一般情況下使用元組:
```
>>> format = "Hello, %s. %s enough for ya?"
>>> values = ("world", "Hot")
>>> print format % values
Hello, world. Hot enough for ya?
```
_注:如果使用列表或者其他序列代替元組,那么序列會被解釋為一個值。只有元組和字典(將在第4章討論)可以格式化一個以上的值。_
格式化字符串的`%s`部分稱為_轉換說明符_(conversion specifier),它們標記了需要插入轉換值的位置。`s`表示值會被格式化為字符串——如果不是字符串,則會用`str`將其轉換為字符串。這個方法對大多數值都有效。其他轉換說明符請參見本章后面的表3-1.
_注:如果要在格式化字符串里面包括百分號,那么必須使用`%%`,這樣Python就不會將百分號誤認為是轉換說明符了。_
如果要格式化實數(浮點數),可以使用`f`說明轉換說明符的類型,同時提供所需要的_精度_:一個句點再加上希望保留的小數位數。因為格式化轉換說明符總是以表示類型的字符結束,所以精度應該放在類型字符前面:
```
>>> format = "Pi with three decimals: %.3f"
>>> from math import pi
>>> print format % pi
Pi with three decimals: 3.142
```
**模板字符串**
string模塊提供另外一種格式化值的方法:模板字符串。它的工作方式類似于很多UNIX Shell里的變量替換。如下所示,`substitute`這個模板方法會用傳遞進來的關鍵字參數`foo`替換字符串中的`$foo`(有關關鍵字參數的詳細信息,請參看第六章):
```
>>> from string import Template
>>> s = Template("$x, glorious $x!")
>>> s.substitute(x="slurm") 'slurm, glorious slurm!'
```
如果替換字段是單詞的一部分,那么參數名就必須用括號括起來,從而準確指明結尾:
```
>>> s = Template("It's ${x}tastic!")
>>> s.substitute(x="slurm") "It's slurmtastic!"
```
可以使用`$$`插入美元符號:
```
>>> s = Template("Make $$ selling $x!")
>>> s.substitute(x="slurm") 'Make $ selling slurm!'
```
除了關鍵字參數之外,還可以使用字典變量提供值/名稱對(參見第四章)。
```
>>> s = Template("A $thing must never $action.") >>> d = {}
>>> d["thing"] = "gentleman"
>>> d["action"] = "show his socks"
>>> s.substitute(d)
'A gentleman must never show his socks.'
```
方法`safe_substitute`不會因缺少值或者不正確使用`$`字符而出錯(更多信息請參見Python庫參考手冊的4.1.2節)。
## 3.3 字符串格式化:完整版
格式化操作符的右操作數可以是任意類型,如果是元組或者映射類型(如字典),那么字符串格式化將會有所不同。我們尚未涉及映射(如字典),在此先了解一下元組。第四章還會詳細介紹映射的格式化。
如果右操作數是元組的話,則其中的每一個元素都會被單獨格式化,每個值都需要一個對應的轉換說明符。
_注:如果需要轉換的元組作為轉換表達式的一部分存在,那么必須將它用圓括號括起來,以避免出錯。_
```
>>> "%s plus %s equals %s" % (1, 1, 2)
'1 plus 1 equals 2'
# Lacks parentheses!
>>> "%s plus %s equals %s" % 1, 1, 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not enough arguments for format string
```
基本的轉換說明符(與此相對應的是完整的轉換說明符,也就是包括映射鍵的說明符,詳細內容參見第四章)包括以下部分。注意,這些項的順序是至關重要的。
(1)`%`字符:標記轉換說明符的開始。
(2)轉換標志(可選): `-` 表示左對齊; `+` 表示在轉換值之前要加上正負號;`" "` (空白字符)表示整數之前保留空格;`0`表示轉換值若位數不夠則用`0`填充。
(3)最小字段寬度(可選):轉換后的字符串至少應該具有該值指定的寬度。如果是`*`,則寬度會從值元組中讀出。
(4)點(`.`)后跟精度值(可選):如果轉化的是實數,精度值就表示出現在小數點后的位數。如果轉換的是字符串,那么該數字就表示_最大字段寬度_。如果是*,那么精度將會從元組中讀出。
(5)轉換類型:參見表3-1。
表3-1 字符串格式化轉換類型
```
d, i 帶符號的十進制整數
o 不帶符號的八進制
u 不帶符號的十進制
x 不帶符號的十六進制(小寫)
X 不帶符號的十六進制(大寫)
e 科學計數法表示的浮點數(小寫)
E 科學計數法表示的浮點數(大寫)
f, F 十進制浮點數
g 如果指數大于-4或者小于精度值則和e相同,其他情況與f相同
G 如果指數大于-4或者小于精度值則和E相同,其他情況與F相同
C 單字符(接受整數或者單字符字符串)
r 字符串(使用repr轉換任意Python對象)
s 字符串(使用str轉換任意Python對象)
```
接下來幾個小節將對轉換說明符的各個元素進行詳細討論。
### 3.3.1 簡單轉換
簡單的轉換只需要寫出轉換類型,使用起來很簡單:
```
>>> "Price of eggs: $%d" % 42
'Price of eggs: $42'
>>> "Hexadecimal price of eggs: %x" % 42
'Hexadecimal price of eggs: 2a'
>>> from math import pi >>> "Pi: %f..." %pi
'Pi: 3.141593...'
>>> "Very inexact estimate of pi: %i" % pi
'Very inexact estimate of pi: 3'
>>> "Using str: %s" % 42L
'Using str: 42'
>>> "Using repr: %r" % 42L
'Using repr: 42L'
```
### 3.3.2 字段寬度和精度
轉換說明符可以包括字段寬度和精度。字段寬度是轉換后的值所保留的最小字符個數,精度(對于數字轉換來說)則是結果中應該包含的小數位數,或者(對于字符串轉換來說)是轉換后的值所能包含的最大字符個數。
這兩個參數都是整數(首先是字段寬度,然后是精度),通過點號(`.`)分隔。雖然兩個都是可選的參數,但如果只給出精度,就必須包含點號:
```
>>> "%10f" % pi # 字段寬10
' 3.141593'
>>> "%10.2f" % pi # 字段寬10,精度2
' 3.14'
>>> "%.2f" % pi # 精度2
'3.14'
>>> "%.5s" % "Guido van Rossum"
'Guido'
```
可以使用`*`(星號)作為字段寬度或者精度(或者兩者都是用`*`),此時數值會從元組參數中讀出:
```
>>> "%.*s" % (5, "Guido van Rossum")
'Guido'
```
### 3.3.3 符號、對齊和用0填充
在字段寬度和精度值之前還可以放置一個“標志”,該標志可以是零、加號、減號或空格。零表示數字將會用`0`進行填充。
```
>>> "%010.2f" % pi
'0000003.14'
```
注意,在`010`中開頭的那個`0`并不意味著字段寬度說明符為八進制數,它只是個普通的Python數值。當使用`010`作為字段寬度說明符的時候,表示字段寬度為`10`,并且用`0`進行填充空位,而不是說字段寬度為`8`:
```
>>> 010
8
```
減號(`-`)用來左對齊數值:
```
>>> "%-10.2f" % pi
'3.14 '
```
可以看到,在數字的右側多出了額外的空格。
而空白(`" "`)意味著在正數前加上空格。這在需要對齊正負數時會很有用:
```
>>> print ("%+5d" % 10) + "\n" + ("%+5d" % -10) +10
-10
```
代碼清單3-1中的代碼將使用星號字段寬度說明符來格式化一張包含水果價格的表格,表格的總寬度由用戶輸入。因為是由用戶提供信息,所以就不能在轉換說明符中將字段寬度硬編碼。使用星號運算符就可以從轉換元組中讀出字段寬度。
```
1 #!/usr/bin/env python
2 # coding=utf-8
3
4 # 使用給定的寬度打印格式化后的價格列表
5
6 width = input("Please enter width: ")
7
8 price_width = 10
9 item_width = width - price_width
10
11 header_format = "%-*s%*s"
12 format = "%-*s%*.2f"
13
14 print "=" * width 15
16 print header_format % (item_width, "Item", price_width, "Price")
17
18 print "-" * width 19
20 print format % (item_width, "Apples", price_width, 0.4)
21 print format % (item_width, "Pears", price_width, 0.5)
22 print format % (item_width, "Cantaloupes", price_width, 1.92)
23 print format % (item_width, "Dried Apricots (16 oz.)", price_width, 8) 24 print format % (item_width, "Prunes (4 lbs.)", price_width, 12)
25
26 print "=" * width
```
Code_Listing 3-1
以下是程序運行示例:
```
Please enter width: 35
===================================
Item Price
-----------------------------------
Apples 0.40
Pears 0.50
Cantaloupes 1.92
Dried Apricots (16 oz.) 8.00
Prunes (4 lbs.) 12.00
===================================
```
## 3.4 字符串方法
前面幾節已經介紹了很多列表的方法,字符串的方法還要豐富得多,這是因為字符串從`string`模塊中“繼承”了很多方法,而在早期版本的Python中,這些方法都是作為函數出現的(如果真的需要的話,還是能找到這些函數的)。
因為字符串的方法是實在太多,在這里只介紹一些特別有用的。全部方法請參見附錄B。在字符串的方法描述中,可以在本章找到關聯到其他方法的參考(用“請參見”標記),或請參見附錄B。
**但是字符串未死**
盡管字符串方法完全來源于`string`模塊,但是這個模塊還包括一些不能作為字符串方法使用的常量和函數。`maketrans`函數就是其中之一,后面會將它和`translate`方法一起介紹。下面是一些有用的字符串常量(對于此模塊的更多介紹,請參見[Python庫參考手冊](http://python.org/doc/lib/module-string.html)的4.1節)。
√ string.digits:包含數字0~9的字符串。
√ string.letters:包含所有字母(大寫或小寫)的字符串。
√ string.lowercase:包含所有小寫字母的字符串。
√ string.printable:包含所有可打印字符的字符串。
√ string.punctuation:包含所有標點的字符串。
√ string.uppercase:包含所有大寫字母的字符串。
字母字符串常量(例如`string.letters`)與地區有關(也就是說,其具體值取決于Python所配置的語言)(在Python3.0中,`string.letters`和其相關內容都會被移除。如果需要則應該使用`string.ascii_letters`常量代替)。如果可以確定自己使用的ASCII,那么可以在變量中使用`ascii_`前綴,例如`string.ascii_letters`。
### 3.4.1 `find`
`find`方法可以在一個較長的字符串中查找子串。它返回子串所在位置的最左端索引。如果沒有找到則返回`-1`。
```
>>> "With a moo-moo here, and a moo-moo there".find("moo") 7
>>> title = "Monty Python's Flying Circus"
>>> title.find("Monty")
0 >>> title.find("Python") # 找到字符串
6
>>> title.find("Flying") 15
>>> title.find("Zirquss") # 未找到字符串
-1
```
在第二章中我們初始了成員資格,我們在`subject`中使用了`"$$$"`表達式建立了一個垃圾郵件過濾器。也可以使用`find`方法(Python2.3以前的版本中也可用,但是`in`操作符只能用來查找字符串中的單個字符):
```
>>> subject = "$$$ Get rich now!!! $$$"
>>> subject.find("$$$")
0
```
_注:字符串的`find`方法并不返回布爾值。如果返回的是`0`,則證明在索引`0`位置找到了子串。_
這個方法還可以接收可選的起始點和結束點參數:
```
>>> subject = "$$$ Get rich now!!! $$$"
>>> subject.find("$$$")
0 >>> subject.find("$$$", 1) # 只提供起始點
20
>>> subject.find("!!!") 16
>>> subject.find("!!!", 0, 16) # 提供起始點和結束點
-1
```
注意,由起始和終止值指定的范圍(第二個和第三個參數)包含第一個索引,但不包含第二個索引。這在Python中是個慣例。
附錄B:`rfind`、`index`、`rindex`、`count`、`startswith`、`endswith`。
### 3.4.2 `join`
`join`方法是非常重要的字符串方法,它是`split`方法的逆方法,用來連接序列中的元素:
```
>>> seq = [1, 2, 3, 4, 5]
>>> sep = "+"
>>> sep.join(seq) # 連接數字列表
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence item 0: expected string, int found
>>> seq = ["1", "2", "3", "4", "5"]
>>> sep.join(seq) # 連接字符串列表
'1+2+3+4+5'
>>> dirs = "", "usr", "bin", "env"
>>> "/".join(dirs)
'/usr/bin/env'
>>> print "C:" + "\\".join(dirs)
C:\usr\bin\env
```
可以看到,需要被連接的序列元素都必須是字符串。注意最后兩個例子中使用了目錄的列表,而在格式化時,根據UNIX和DOS/Windows的約定,使用了不同的分隔符號(在DOS版本中還增加了驅動器名)。
請參見:`split`。
### 3.4.3 `lower`
`lower`方法返回字符串的小寫字母版。
```
>>> "Trondheim Hammer Dance".lower()
'trondheim hammer dance'
```
如果想要編寫“不區分大小寫”的代碼的話,那么這個方法就派上用場了——代碼會忽略大小寫狀態。例如,如果想在列表中查找一個用戶名是否存在:列表包含字符串`"gumby"`,而用戶輸入的是`"Gumby"`,就找不到了:
```
>>> if "Gumby" in ["gumby", "smith", "jones"]:
print "Found it!"
...
>>>
```
如果存儲的是`"Gumby"`而用戶輸入`"gumby"`甚至是`"GUMBY"`,結果也是一樣的。解決方法就是在存儲和搜索時把所有名字都轉換為小寫。代碼如下:
```
>>> name = "Gumby"
>>> names = ["gumby", "smith", "jones"]
>>> if name.lower() in names:
print "Found it!"
...
Found it!
```
請參見:`translate`。
附錄B:`islower`、`capitalize`、`swapcase`、`title`、`istitle`、`upper`、`isupper`。
**標題轉換**
和`lower`方法相關的是`title`方法(參見附錄B),它會將字符串轉換為標題——也就是所有單詞的首字母大寫,而其他字母小寫。但是它使用的單詞劃分方法可能會得到并不自然的結果:
```
>>> "that's all folks".title()
"That'S All Folks"
```
再介紹另外一個`string`模塊的`capwords`函數:
```
>>> import string
>>> string.capwords("that's all, folks")
"That's All, Folks"
```
當然,如果要得到正確首字母大寫的標題(這要根據你的風格而定,可能要小寫冠詞、連詞及5個字母以下的介詞等),那么還是得自己把握。
### 3.4.4 `replace`
`replace`方法返回某字符串的所有匹配項均被替換之后得到字符串。
```
>>> "This is a test".replace("is", "eez")
'Theez eez a test'
```
如果曾經用過文字處理程序中的“查找并替換”功能的話,就不會質疑這個方法的用處了。
請參見:`translate`。
附錄B:`expandtabs`。
### 3.4.5 `split`
這是一個非常重要的字符串方法,它是`join`的逆方法,用來將字符串分隔成序列。
```
>>> "1+2+3+4+5".split("+")
['1', '2', '3', '4', '5']
>>> "/usr/bin/env".split("/")
['', 'usr', 'bin', 'env']
>>> "Using the default".split()
['Using', 'the', 'default']
```
注意,如果不提供任何分隔符,程序會把所有空格作為分隔符(空格、制表、換行等)。
請參見:`join`。
附錄B:`rsplit`、`splitlines`。
### 3.4.6 `strip`
`strip`方法返回去除兩側(不包括內部)空格的字符串:
```
>>> " internal whitespace is kept ".strip()
'internal whitespace is kept'
```
它和`lower`方法一起使用的話就可以很方便的對比輸入的和存儲的值。讓我們回到`lower`部分中的用戶名的例子,假設用戶在輸入名字時無意中在名字后面加上了空格:
```
>>> names = ["gumby", "smith", "jones"]
>>> name = "gumby "
>>> if name in names:
print "Found it!"
...
>>> if name.strip() in names:
print "Found it!"
...
Found it!
```
也可以指定需要去除的字符,將它們列為參數即可。
```
>>> "*** SPAM * for * everyone!!! ***".strip(" *!")
'SPAM * for * everyone'
```
這個方法只會去除兩側的字符,所以字符串中的星號沒有被去掉。
附錄B:`lstrip`、`rstrip`。
### 3.4.7 `translate`
`translate`方法和`replace`方法一樣,可以替換字符串中的某些部分,但是和前者不同的是,`translate`方法只處理單個字符。它的優勢在于可以同時進行多個替換,有些時候比`replace`效率高得多。
使用這個方法的方式有很多(比如替換換行符或者其他因平臺而異的特殊字符)。但是讓我們考慮一個簡單的例子(很簡單的例子):假設需要將純正的英文文本轉換為帶有德國口音的版本。為此,需要把字符`c`替換為`k`把`s`替換為`z`。
在使用`translate`轉換之前,需要先完成一張_轉換表_。轉換表中是以某字符替換某字符的對應關系。因為這個表(事實上是字符串)有多達256個項目,我們還是不要自己寫了,使用`string`模塊里面的`maketrans`函數就行。
`maketrans`函數接受兩個參數:兩個等長的字符串,表示第一個字符串中的每個字符都用第二個字符串中相同位置的字符替換。明白了嗎?來看一個簡單的例子,代碼如下:
```
>>> from string import maketrans
>>> table = maketrans("cs", "kz")
```
**轉換表中都有什么**
轉換表是包含替換ASCII字符集中256個字符的替換字母的字符串。
```
>>> table = maketrans("cs", "kz")
>>> len(table) 256
>>> table[97:123]
'abkdefghijklmnopqrztuvwxyz'
>>> maketrans("", "")[97:123]
'abcdefghijklmnopqrstuvwxyz'
```
正如你看到的,我已經把小寫字母部分的表提取出來了。看一下這個表和空轉換(沒有改變任何東西)中的字母表。空轉換包含一個普通的字母表,而在它前面的代碼中,字母`c`和`s`分別被替換為`k`和`z`。
創建這個表以后,可以將它用作`translate`方法的參數,進行字符串的轉換如下:
```
>>> "this is an incredible test".translate(table)
'thiz iz an inkredible tezt'
```
`translate`的第二個參數是可選的,這個參數是用來指定需要刪除的字符。例如,如果想要模擬一句語速超快的德國語,可以刪除所有空格:
```
>>> "this is an incredible test".translate(table, " ") 'thizizaninkredibletezt'
```
請參見:`replace`、`lower`。
## 3.5 小結
本章介紹了字符串的兩種非常重要的使用方式。
**字符串格式化**:求模操作符(`%`)可以用來將其他值轉換為包含轉換標志的字符串,例如`%s`。它還能用來對值進行不同方式的格式化,包括左右對齊、設定字段寬度以及精度值,增加符號(正負號)或者左填充數字`0`等。
**字符串方法**:字符串有很多方法。有些非常有用(比如`split`和`join`),有些則用的很少(比如`istitle`或者`capitalize`)。
### 3.5.1 本章的新函數
本章新涉及的函數如表3-2所示。
表3-2 本章的新函數
```
string.capwords(s[, sep]) 使用split函數分隔字符串s(以sep為分隔符),使用capitalize函數將分割得到的各單詞首字母大寫,并且使用join函數以sep為分隔符將各單詞連接起來。
string.maketrans(from, to) 創建用于轉換的轉換表。
```
### 3.5.2 接下來學什么
列表、字符串和字典是Python中最重要的3種數據類型。列表和字符串已經學習過了,那么下面是什么呢?下一章中的主要內容是字典,以及字典如何支持索引以及其他方式的鍵(比如字符串和元組)。字典也提供了一些方法,但是數量沒有字符串多。