字符串(str)
字符串的定義
- 字符串 就是 一串字符,是編程語言中表示文本的數據類型
- 在 Python 中可以使用 一對雙引號 " 或者 一對單引號 ' 定義一個字符串
- 可以使用 索引 獲取一個字符串中 指定位置的字符,索引計數從 0 開始
- 也可以使用 for 循環遍歷 字符串中每一個字符
#!/usr/bin/env python3
# -*-coding:utf-8-*-
"""
@author:fyh
@time:2019/5/31
"""
str1 = "hello python"
for c in str1:
print(c, end='\t')
# 運行結果:h e l l o p y t h o n
字符串的常用操作
#!/usr/bin/env python3
# -*-coding:utf-8-*-
"""
@author:fyh
@time:2019/5/31
"""
# 1 * 重復輸出字符串
print('hello' * 2)
# 2 [] ,[:] 通過索引獲取字符串中字符
print('helloworld'[2:])
# 3 in 成員運算符 - 如果字符串中包含給定的字符返回 True
print('el' in 'hello')
# 4 % 格式字符串
print('alex is a good teacher')
print('%s is a good teacher' % 'alex')
# 5 + 字符串拼接
a = '123'
b = 'abc'
c = '789'
d1 = a + b + c
print(d1) # +效率低,該用join
# join效率高
d2 = ''.join([a, b, c])
print(d2)
字符串相關的函數
常用函數
# string.upper() 轉換 string 中的小寫字母為大寫
# string.lower() 轉換 string 中所有大寫字符為小寫
# string.startswith(obj, beg=0,end=len(string)) 檢查字符串是否是以obj開頭,是則返回True,否則返回 False。如果beg 和 end 指定值,則在指定范圍內檢查.
# string.endswith(obj, beg=0, end=len(string)) 檢查字符串是否以 obj 結束,如果beg 或者 end 指定則檢查指定的范圍內是否以 obj 結束,如果是,返回 True,否則返回 False.
# string.replace(str1, str2, num=string.count(str1)) 把 string 中的 str1 替換成 str2,如果 num 指定,則替換不超過 num 次.
# string.strip([obj]) 在 string 上執行 lstrip()和 rstrip() 去除空格
# string.split(str="", num=string.count(str)) 以 str 為分隔符切片 string,如果 num有指定值,則僅分隔 num 個子字符串
# string.find(str, beg=0, end=len(string)) 檢測 str 是否包含在 string 中,如果 beg 和 end 指定范圍,則檢查是否包含在指定范圍內,如果是返回開始的索引值,否則返回-1
# string.encode(encoding='UTF-8', errors='strict') 以 encoding 指定的編碼格式編碼 string,如果出錯默認報一個ValueError 的異常,除非 errors 指定的是ignore或者replace
# string.decode(encoding='UTF-8', errors='strict') 以 encoding 指定的編碼格式解碼 string,如果出錯默認報一個ValueError的異常,除非errors指定的是'ignore'或 者'replace'
# string.join(seq) 以 string 作為分隔符,將 seq 中所有的元素(的字符串表示)合並為一個新的字符串
# string.format() 格式化輸出
其它函數
# string.capitalize() 把字符串的第一個字符大寫
# string.center(width) 返回一個原字符串居中,並使用空格填充至長度 width 的新字符串
# string.count(str, beg=0, end=len(string)) 返回 str 在 string 里面出現的次數,如果 beg 或者 end 指定則返回指定范圍內 str 出現的次數
# string.expandtabs(tabsize=8) 把字符串 string 中的 tab 符號轉為空格,tab 符號默認的空格數是8。
# string.index(str, beg=0, end=len(string)) 跟find()方法一樣,只不過如果str不在 string中會報一個異常.
# string.isalnum() 如果 string 至少有一個字符並且所有字符都是字母或數字則返回 True,否則返回 False
# string.isalpha() 如果 string 至少有一個字符並且所有字符都是字母則返回 True,否則返回 False
# string.isdecimal() 如果 string 只包含十進制數字則返回 True 否則返回 False.
# string.isdigit() 如果 string 只包含數字則返回 True 否則返回 False.
# string.islower() 如果 string 中包含至少一個區分大小寫的字符,並且所有這些(區分大小寫的)字符都是小寫,則返回 True,否則返回 False
# string.isnumeric() 如果 string 中只包含數字字符,則返回 True,否則返回 False
# string.isspace() 如果 string 中只包含空格,則返回 True,否則返回 False.
# string.istitle() 如果 string 是標題化的(見 title())則返回 True,否則返回 False
# string.isupper() 如果 string 中包含至少一個區分大小寫的字符,並且所有這些(區分大小寫的)字符都是大寫,則返回 True,否則返回 False
# string.ljust(width) 返回一個原字符串左對齊,並使用空格填充至長度 width 的新字符串
# string.lstrip() 截掉 string 左邊的空格
# string.maketrans(intab, outtab]) maketrans() 方法用於創建字符映射的轉換表,對於接受兩個參數的最簡單的調用方式,第一個參數是字符串,表示需要轉換的字符,第二個參數也是字符串表示轉換的目標。
# max(str) 返回字符串 str 中最大的字母。
# min(str) 返回字符串 str 中最小的字母。
# string.partition(str) 有點像 find()和 split()的結合體,從 str 出現的第一個位置起,把 字 符 串 string 分 成 一 個 3 元 素 的 元 組 (string_pre_str,str,string_post_str),如果 string 中不包含str 則 string_pre_str == string.
# string.rfind(str, beg=0,end=len(string) ) 類似於 find()函數,不過是從右邊開始查找.
# string.rindex( str, beg=0,end=len(string)) 類似於 index(),不過是從右邊開始.
# string.rjust(width) 返回一個原字符串右對齊,並使用空格填充至長度 width 的新字符串
# string.rpartition(str) 類似於 partition()函數,不過是從右邊開始查找.
# string.rstrip() 刪除 string 字符串末尾的空格.
# string.splitlines(num=string.count('\n')) 按照行分隔,返回一個包含各行作為元素的列表,如果 num 指定則僅切片 num 個行.
# string.swapcase() 翻轉 string 中的大小寫
# string.title() 返回"標題化"的 string,就是說所有單詞都是以大寫開始,其余字母均為小寫(見 istitle())
# string.translate(str, del="") 根據 str 給出的表(包含 256 個字符)轉換 string 的字符,要過濾掉的字符放到 del 參數中
案例:
#!/usr/bin/env python3
# -*-coding:utf-8-*-
"""
@author:fyh
@time:2019/5/31
"""
name = "hello world"
# 小寫字母變為大寫
print(name.upper()) # HELLO WORLD
print(name) # hello world
# 把大寫字母變小寫
name2 = "HELLO WORLD"
print(name2.lower()) # hello world
print(name2) # HELLO WORLD
# 判斷是否以。。。開頭
name3 = "hello"
print(name3.startswith("he")) # True
# 檢查字符串是否以。。結尾
print(name3.endswith("lo")) # True
# replace 替換
name4 = "python"
print(name4.replace('th', 'aa')) # pyaaon
# 去除空格
name5 = " bbcc "
print(name5.strip()) # bbcc
# 分割
str1 = "aa|bb|cc|dd"
print(str1.split('|')) # ['aa', 'bb', 'cc', 'dd']
# 查找 返回開始的索引值
str2 = "we are family"
print(str2.find("are")) # 3
# join 拼接
lst1 = ['aa', 'bb', 'cc', 'dd']
print("-".join(lst1)) # aa-bb-cc-dd
字符串切片
- 切片 使用 索引值 來限定范圍,根據 步長 從原序列中 取出一部分 元素組成新序列
- 切片 方法適用於 字符串、列表、元組
- 切片的語法表達式為:[start_index : end_index : step],其中:
- start_index:起始索引
- end_index:結束索引
- step:步長
切片操作是指按照步長,截取從起始索引到結束索引,但不包含結束索引(也就是結束索引減1)的所有元素。
切片不會改變原對象,而是重新生成了一個新的對象
#!/usr/bin/env python3
# -*-coding:utf-8-*-
"""
@author:fyh
@time:2019/5/31
"""
str1 = "hello world"
print(str1[1:6])
# 結果是ello
print(str1[1:6:2])
# 結果是el
print(str1[2:])
# 結果是llo world
# 保留start_index,但省略end_index,這樣會從起始索引開始,切到最后一個元素為止
print(str1[:5])
# 結果是hello
# 省略start_index,保留end_index,這樣會從第一個元素開始,切到end_index - 1的元素為止
print(str1[-1:-6:-1])
# 結果是dlrow
print(str1[1:6:-1])
# 結果為空
# 切片時,一定要保證start_index到end_index的方向與步長step的方向同向,否則會切出空的序列
字符串拼接
Python的字符串格式化有三種方式: 百分號方式、format方式、f-strings方式
百分號方式
格式:%[(name)][flags][width].[precision]typecode
-
(name) 可選,用於選擇指定的key
-
flags 可選,可供選擇的值有:
-
- + 右對齊;正數前加正好,負數前加負號;
- - 左對齊;正數前無符號,負數前加負號;
- 空格 右對齊;正數前加空格,負數前加負號;
- 0 右對齊;正數前無符號,負數前加負號;用0填充空白處
-
width 可選,占有寬度
-
precision 可選,小數點后保留的位數
-
typecode 必選
-
- s,獲取傳入對象的__str__方法的返回值,並將其格式化到指定位置
- r,獲取傳入對象的__repr__方法的返回值,並將其格式化到指定位置
- c,整數:將數字轉換成其unicode對應的值,10進制范圍為 0 <= i <= 1114111(py27則只支持0-255);字符:將字符添加到指定位置
- o,將整數轉換成 八進制表示,並將其格式化到指定位置
- x,將整數轉換成十六進制表示,並將其格式化到指定位置
- d,將整數、浮點數轉換成 十 進制表示,並將其格式化到指定位置
- e,將整數、浮點數轉換成科學計數法,並將其格式化到指定位置(小寫e)
- E,將整數、浮點數轉換成科學計數法,並將其格式化到指定位置(大寫E)
- f, 將整數、浮點數轉換成浮點數表示,並將其格式化到指定位置(默認保留小數點后6位)
- F,同上
- g,自動調整將整數、浮點數轉換成 浮點型或科學計數法表示(超過6位數用科學計數法),並將其格式化到指定位置(如果是科學計數則是e;)
- G,自動調整將整數、浮點數轉換成浮點型或科學計數法表示(超過6位數用科學計數法),並將其格式化到指定位置(如果是科學計數則是E;)
- %,當字符串中存在格式化標志時,需要用 %%表示一個百分號
注:Python中百分號格式化是不存在自動將整數轉換成二進制表示的方式
案例:
tpl = "i am %s" % "alex"
tpl = "i am %s age %d" % ("alex", 18)
tpl = "i am %(name)s age %(age)d" % {"name": "alex", "age": 18}
tpl = "percent %.2f" % 99.97623
tpl = "i am %(pp).2f" % {"pp": 123.425556, }
tpl = "i am %.2f %%" % 123.425556
format方式
格式:[[fill]align][sign][#][0][width][,][.precision][type]
-
-
fill 【可選】空白處填充的字符
-
align 【可選】對齊方式(需配合width使用)
-
- <,內容左對齊
- >,內容右對齊(默認)
- =,內容右對齊,將符號放置在填充字符的左側,且只對數字類型有效。 即使:符號+填充物+數字
- ^,內容居中
-
sign 【可選】有無符號數字
-
- +,正號加正,負號加負;
- -,正號不變,負號加負;
- 空格 ,正號空格,負號加負;
-
# 【可選】對於二進制、八進制、十六進制,如果加上#,會顯示 0b/0o/0x,否則不顯示
-
, 【可選】為數字添加分隔符,如:1,000,000
-
width 【可選】格式化位所占寬度
-
.precision 【可選】小數位保留精度
-
type 【可選】格式化類型
-
-
傳入” 字符串類型 “的參數
-
- s,格式化字符串類型數據
- 空白,未指定類型,則默認是None,同s
-
傳入“ 整數類型 ”的參數
-
- b,將10進制整數自動轉換成2進制表示然后格式化
- c,將10進制整數自動轉換為其對應的unicode字符
- d,十進制整數
- o,將10進制整數自動轉換成8進制表示然后格式化;
- x,將10進制整數自動轉換成16進制表示然后格式化(小寫x)
- X,將10進制整數自動轉換成16進制表示然后格式化(大寫X)
-
-
-
-
- 傳入“ 浮點型或小數類型 ”的參數
-
-
-
-
- e, 轉換為科學計數法(小寫e)表示,然后格式化;
- E, 轉換為科學計數法(大寫E)表示,然后格式化;
- f , 轉換為浮點型(默認小數點后保留6位)表示,然后格式化;
- F, 轉換為浮點型(默認小數點后保留6位)表示,然后格式化;
- g, 自動在e和f中切換
- G, 自動在E和F中切換
- %,顯示百分比(默認顯示小數點后6位)
-
-
案例:
tpl = "i am {}, age {}, {}".format("seven", 18, 'alex')
tpl = "i am {}, age {}, {}".format(*["seven", 18, 'alex'])
tpl = "i am {0}, age {1}, really {0}".format("seven", 18)
tpl = "i am {0}, age {1}, really {0}".format(*["seven", 18])
tpl = "i am {name}, age {age}, really {name}".format(name="seven", age=18)
tpl = "i am {name}, age {age}, really {name}".format(**{"name": "seven", "age": 18})
tpl = "i am {0[0]}, age {0[1]}, really {0[2]}".format([1, 2, 3], [11, 22, 33])
tpl = "i am {:s}, age {:d}, money {:f}".format("seven", 18, 88888.1)
tpl = "i am {:s}, age {:d}".format(*["seven", 18])
tpl = "i am {name:s}, age {age:d}".format(name="seven", age=18)
tpl = "i am {name:s}, age {age:d}".format(**{"name": "seven", "age": 18})
tpl = "numbers: {:b},{:o},{:d},{:x},{:X}, {:%}".format(15, 15, 15, 15, 15, 15.87623, 2)
tpl = "numbers: {:b},{:o},{:d},{:x},{:X}, {:%}".format(15, 15, 15, 15, 15, 15.87623, 2)
tpl = "numbers: {0:b},{0:o},{0:d},{0:x},{0:X}, {0:%}".format(15)
tpl = "numbers: {num:b},{num:o},{num:d},{num:x},{num:X}, {num:%}".format(num=15)
f-strings方式
f-strings 提供一種簡潔易讀的方式, 可以在字符串中包含 Python 表達式. f-strings 以字母 'f' 或 'F' 為前綴, 格式化字符串使用一對單引號、雙引號、三單引號、三雙引號. 格式化字符串中。
#!/usr/bin/env python
# -*-coding:utf-8-*-
"""
@author:fyh
@time:2019/6/5
"""
name = '豪仔'
age = 26
format_string1 = f'我的名字是 {name}, 我的年齡是 {age}'
print(format_string1) # 我的名字是 豪仔, 我的年齡是 26
format_string2 = f"我的名字是 {name}, 我的年齡是 {age}"
print(format_string2) # 我的名字是 豪仔, 我的年齡是 26
format_string3 = F'''我的名字是 {name}, 我的年齡是 {age}'''
print(format_string3) # 我的名字是 豪仔, 我的年齡是 26
format_string4 = F"""我的名字是 {name}, 我的年齡是 {age}"""
print(format_string4) # 我的名字是 豪仔, 我的年齡是 26
# 花括號中的數字會先運算
format_string5 = f'3 + 5 = {3 + 5}'
print(format_string5) # 3 + 5 = 8
a = 10
b = 20
format_string6 = f'3 + 5 = {a + b}'
print(format_string6) # 3 + 5 = 30
# 兩個花括號會被替換為一個花括號, 注意{{}} 不表示表達式
format_string7 = F'我的名字是 {{name}}, 我的年齡是 {{age}}'
print(format_string7)
字節型(bytes)
Python3最重要的新特性之一是對字符串和二進制數據流做了明確的區分。文本總是Unicode,由str類型表示,二進制數據則由bytes類型表示。Python3不會以任意隱式的方式混用str和bytes,你不能拼接字符串和字節流,也無法在字節流里搜索字符串(反之亦然),也不能將字符串傳入參數為字節流的函數(反之亦然)。
回顧編碼的發展史
在計算機歷史的早期,美國為代表的英語系國家主導了整個計算機行業,26個英文字母組成了多樣的英語單詞、語句、文章。因此,最早的字符編碼規范是ASCII碼,一種8位即1個字節的編碼規范,它可以涵蓋整個英語系的編碼需要。
編碼是什么?編碼就是把一個字符用一個二進制來表示。我們都知道,所有的東西,不管是英文、中文還是符號等等,最終存儲在磁盤上都是01010101這類東西。在計算機內部,讀取和存儲數據歸根結底,處理的都是0和1組成的比特流。問題來了,人類看不懂這些比特流,如何讓這些010101對人類變得可讀呢?於是出現了字符編碼,它是個翻譯機,在計算機內部某個地方,透明的幫我們將比特流翻譯成人類可以直接理解的文字。對於一般用戶,不需要知道這個過程是什么原理,是怎么執行的。但是對於程序員卻是個必須搞清楚的問題。
以ASCII編碼為例,它規定1個字節8個比特位代表1個字符的編碼,也就是“00000000”這么寬,一個一個字節的解讀。例如:01000001表示大寫字母A,有時我們會“偷懶"的用65這個十進制來表示A在ASCII中的編碼。8個比特位,可以沒有重復的最多表示2的8次方(255)個字符。
后來,計算機得到普及,中文、日文、韓文等等國家的文字需要在計算機內表示,ASCII的255位遠遠不夠,於是標准組織制定出了叫做UNICODE的萬國碼,它規定任何一個字符(不管哪國的)至少以2個字節表示,可以更多。其中,英文字母就是用2個字節,而漢字是3個字節。這個編碼雖然很好,滿足了所有人的要求,但是它不兼容ASCII,同時還占用較多的空間和內存。因為,在計算機世界更多的字符是英文字母,明明可以1個字節就能夠表示,非要用2個。
於是UTF-8編碼應運而生,它規定英文字母系列用1個字節表示,漢字用3個字節表示等等。因此,它兼容ASCII,可以解碼早期的文檔。UTF-8很快就得到了廣泛的應用。
在編碼的發展歷程中,我國還創造了自己的編碼方式,例如GBK,GB2312,BIG5。他們只局限於在國內使用,不被國外認可。在GBK編碼中,中文漢字占2個字節。
bytes和str的異同
回到bytes和str的身上。bytes是一種比特流,它的存在形式是01010001110這種。我們無論是在寫代碼,還是閱讀文章的過程中,肯定不會有人直接閱讀這種比特流,它必須有一個編碼方式,使得它變成有意義的比特流,而不是一堆晦澀難懂的01組合。因為編碼方式的不同,對這個比特流的解讀也會不同,對實際使用造成了很大的困擾。下面讓我們看看Python是如何處理這一系列編碼問題的:
>>> s = "中文"
>>> s
'中文'
>>> type(s)
<class 'str'>
>>> b = bytes(s, encoding='utf-8')
>>> b
b'\xe4\xb8\xad\xe6\x96\x87'
>>> type(b)
<class 'bytes'>
從例子可以看出,s是個字符串類型。Python有個內置函數bytes()可以將字符串str類型轉換成bytes類型,b實際上是一串01的組合,但為了在ide環境中讓我們相對直觀的觀察,它被表現成了b'\xe4\xb8\xad\xe6\x96\x87'這種形式,開頭的b表示這是一個bytes類型。\xe4是十六進制的表示方式,它占用1個字節的長度,因此”中文“被編碼成utf-8后,我們可以數得出一共用了6個字節,每個漢字占用3個,這印證了上面的論述。在使用內置函數bytes()的時候,必須明確encoding的參數,不可省略。
我們都知道,字符串類str里有一個encode()方法,它是從字符串向比特流的編碼過程。而bytes類型恰好有個decode()方法,它是從比特流向字符串解碼的過程。除此之外,我們查看Python源碼會發現bytes和str擁有幾乎一模一樣的方法列表,最大的區別就是encode和decode。
從實質上來說,字符串在磁盤上的保存形式也是01的組合,也需要編碼解碼。
如果,上面的闡述還不能讓你搞清楚兩者的區別,那么記住下面兩幾句話:
- 在將字符串存入磁盤和從磁盤讀取字符串的過程中,Python自動地幫你完成了編碼和解碼的工作,你不需要關心它的過程。
- 使用bytes類型,實質上是告訴Python,不需要它幫你自動地完成編碼和解碼的工作,而是用戶自己手動進行,並指定編碼格式。
- Python已經嚴格區分了bytes和str兩種數據類型,你不能在需要bytes類型參數的時候使用str參數,反之亦然。這點在讀寫磁盤文件時容易碰到。
在bytes和str的互相轉換過程中,實際就是編碼解碼的過程,必須顯式地指定編碼格式。
#!/usr/bin/env python3
# -*-coding:utf-8-*-
"""
@author:fyh
@time:2019/5/31
"""
# gbk編碼的bytes
b = bytes('中國', encoding='gbk')
print(b) # b'\xd6\xd0\xb9\xfa'
print(type(b)) # <class 'bytes'>
str1 = str(b)
print(str1) # b'\xd6\xd0\xb9\xfa'
print(type(str1)) # <class 'str'>
# 指定編碼格式
str2 = str(b, encoding='gbk')
print(str2) # 中國
print(type(str2)) # <class 'str'>
# 再把字符串str2轉換為utf8編碼格式的bytes類型:
b2 = bytes(str2, encoding='utf8')
print(b2) # b'\xe4\xb8\xad\xe5\x9b\xbd'
print(type(b2)) # <class 'bytes'>
encode和decode
上面說過了,python3默認的編碼時Unicode,用字符串表示,二進制數據由bytes類型表示。
- encode:將str轉換為bytes,是一個編碼的過程
- decode:將bytes轉換為str,是一個解碼的過程
#!/usr/bin/env python3
# -*-coding:utf-8-*-
"""
@author:fyh
@time:2019/5/31
"""
"""
其中decode()與encode()方法可以接受參數, 其聲明分別為:
bytes.decode(encoding="utf-8", errors="strict")
str.encode(encoding="utf-8", errors="strict")
其中 encoding是指在解碼/編碼(動詞)過程中使用的字符編碼(名詞)
errors是指錯誤的處理方案,errrors參數默認值是strict(嚴格的)意味着如果編解碼出錯將會拋出UnicodeError
如果想忽略編解碼錯誤 可以將errors設置為ignore
"""
# 編碼 encode
str1 = "中國"
str1_utf8 = str1.encode(encoding='utf8')
print(str1_utf8) # b'\xe4\xb8\xad\xe5\x9b\xbd'
print(type(str1_utf8)) # <class 'bytes'>
str1_gbk = str1.encode(encoding="gbk")
print(str1_gbk) # b'\xd6\xd0\xb9\xfa'
print(type(str1_gbk)) # <class 'bytes'>
# 解碼 decode
str2 = str1_utf8.decode(encoding="utf8")
print(str2) # 中國
print(type(str2)) # <class 'str'>
str3 = str1_gbk.decode(encoding="gbk")
print(str3) # 中國
print(type(str3)) # <class 'str'>
編碼和解碼時,對應的編碼方式要一致
對數據的編碼解碼工作是通過返回值查看到最終結果的,前面的數據對象並不受影響