8.Python基礎(chǔ)語法---04字符編碼與字符串格式化

字符編碼

字符串是一種數(shù)據(jù)類型,但字符串比較特殊的是還有一個編碼問題。

亂碼起因

計算機只能處理數(shù)字,如果要處理文本,就必須先把文本轉(zhuǎn)換為數(shù)字才能處理。
計算機在設(shè)計時采用8個比特(bit)作為一個字節(jié)(byte)所以:一個字節(jié)能表示的最大的整數(shù)就是255(二進(jìn)制11111111=十進(jìn)制255;28 -1)。
要表示更大的整數(shù),就必須用更多的字節(jié)。比如兩個字節(jié)可以表示的最大整數(shù)是65535(216 - 1),4個字節(jié)可以表示的最大整數(shù)是4294967295(232 -1)

老美在發(fā)明計算機的時候,只有128個字符被編碼到計算機里,也就是大小寫英文字母、數(shù)字和一些符號,這個編碼表被稱為ASCII編碼,比如大寫字母S的編碼是83,小寫字母s的編碼是115。ASCII編碼對照表

ASCII ((American Standard Code for Information Interchange): 美國信息交換標(biāo)準(zhǔn)代碼)是基于拉丁字母的一套電腦編碼系統(tǒng),主要用于顯示現(xiàn)代英語和其他西歐語言。它是最通用的信息交換標(biāo)準(zhǔn),并等同于國際標(biāo)準(zhǔn)ISO/IEC 646。ASCII第一次以規(guī)范標(biāo)準(zhǔn)的類型發(fā)表是在1967年,最后一次更新則是在1986年,到目前為止共定義了128個字符

ASCII一共才128個字符,用一個字節(jié)就可以足夠編碼(一個字節(jié)有256中字符可以編碼)。

處理中文顯然一個字節(jié)是不夠的,至少需要兩個字節(jié),而且還不能和ASCII編碼沖突,所以,中國制定了GB2312編碼,用來把中文編進(jìn)去。

GB2312編碼:1981年5月1日發(fā)布的簡體中文漢字編碼國家標(biāo)準(zhǔn)。GB2312對漢字采用雙字節(jié)編碼,收錄7445個圖形字符,其中包括6763個漢字。

全世界有上百種語言,日本把日文編到Shift_JIS里,韓國把韓文編到Euc-kr里,各國有各國的標(biāo)準(zhǔn),就會不可避免地出現(xiàn)沖突,結(jié)果就是,在多語言混合的文本中,顯示出來會有亂碼。
因此,Unicode應(yīng)運而生。Unicode把所有語言都統(tǒng)一到一套編碼里,這樣就不會再有亂碼問題了。

Unicode編碼

Unicode標(biāo)準(zhǔn)也在不斷發(fā)展,最常用的是用兩個字節(jié)表示一個字符(如果要用到非常偏僻的字符,就需要4個字節(jié))。現(xiàn)代操作系統(tǒng)和大多數(shù)編程語言都直接支持Unicode。
ASCII編碼和Unicode編碼的區(qū)別:ASCII編碼是1個字節(jié),而Unicode編碼通常是2個字節(jié)。
字母A用ASCII編碼是十進(jìn)制的65,二進(jìn)制的01000001;
字符0用ASCII編碼是十進(jìn)制的48,二進(jìn)制的00110000,注意字符'0'和整數(shù)0是不同的;
漢字已經(jīng)超出了ASCII編碼的范圍,用Unicode編碼是十進(jìn)制的20013,二進(jìn)制的01001110 00101101。

你可以猜測,如果把ASCII編碼的A用Unicode編碼,只需要在前面補0就可以,因此,A的Unicode編碼是00000000 01000001。
問題來了:如果統(tǒng)一成Unicode編碼,亂碼問題從此消失了。
但是,如果文本基本上全部是英文的話,用Unicode編碼比ASCII編碼需要多一倍的存儲空間,在存儲和傳輸上就十分不劃算。本著節(jié)約的精神,又出現(xiàn)了把Unicode編碼轉(zhuǎn)化為“可變長編碼”的UTF-8編碼。

UTF-8編碼

UTF-8 就是在互聯(lián)網(wǎng)上使用最廣的一種 Unicode 的實現(xiàn)方式。其他實現(xiàn)方式還包括 UTF-16(字符用兩個字節(jié)或四個字節(jié)表示)和 UTF-32(字符用四個字節(jié)表示),不過在互聯(lián)網(wǎng)上基本不用。
重復(fù)一遍,這里的關(guān)系是,UTF-8 是 Unicode 的實現(xiàn)方式之一。

UTF-8 最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節(jié)表示一個符號,根據(jù)不同的符號而變化字節(jié)長度。常用的英文字母被編碼成1個字節(jié),漢字通常是3個字節(jié),只有很生僻的字符才會被編碼成4個字節(jié)。如果你要傳輸?shù)奈谋景罅坑⑽淖址肬TF-8編碼就能節(jié)省空間:

UTF-8 的編碼規(guī)則很簡單,只有二條:

  1. 對于單字節(jié)的符號,字節(jié)的第一位設(shè)為0,后面7位為這個符號的 Unicode 碼。因此對于英語字母,UTF-8 編碼和 ASCII 碼是相同的。
  2. 對于n字節(jié)的符號(n > 1),第一個字節(jié)的前n位都設(shè)為1,第n + 1位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒有提及的二進(jìn)制位,全部為這個符號的 Unicode 碼。

下表總結(jié)了編碼規(guī)則,字母x表示可用編碼的位。

Unicode符號范圍(十六進(jìn)制) UTF-8編碼方式 (二進(jìn)制)
0000 0000 ~ 0000 007F 0xxxxxxx
0000 0080 ~ 0000 07FF 110xxxxx 10xxxxxx
0000 0800 ~ 0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000 ~ 0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

跟據(jù)上表,解讀 UTF-8 編碼非常簡單。如果一個字節(jié)的第一位是0,則這個字節(jié)單獨就是一個字符;如果第一位是1,則連續(xù)有多少個1,就表示當(dāng)前字符占用多少個字節(jié)。

下面,還是以漢字為例,演示如何實現(xiàn) UTF-8 編碼。

的 Unicode 是6D6A(110110101101010),根據(jù)上表,可以發(fā)現(xiàn)6D6A處在第三行的范圍內(nèi)(0000 0800 - 0000 FFFF),因此的 UTF-8 編碼需要三個字節(jié),即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,從的最后一個二進(jìn)制位開始,依次從后向前填入格式中的x,多出的位補0。這樣就得到了,的 UTF-8 編碼是11100110 10110101 10101010,轉(zhuǎn)換成十六進(jìn)制就是E6B5AA

字符 ASCII Unicode UTF-8
A 01000001 00000000 01000001 01000001
無法表示 01101101 01101010 11100110 10110101 10101010

UTF-8編碼有一個額外的好處,就是ASCII編碼實際上可以被看成是UTF-8編碼的一部分,所以,大量只支持ASCII編碼的歷史遺留軟件可以在UTF-8編碼下繼續(xù)工作。

擴展

計算機系統(tǒng)通用的字符編碼工作方式:
在計算機內(nèi)存中,統(tǒng)一使用Unicode編碼,當(dāng)需要保存到硬盤或者需要傳輸?shù)臅r候,就轉(zhuǎn)換為UTF-8編碼。
舉個例子
用記事本編輯的時候,從文件讀取的UTF-8字符被轉(zhuǎn)換為Unicode字符到內(nèi)存里,編輯完成后,保存的時候再把Unicode轉(zhuǎn)換為UTF-8保存到文件:


image.png

瀏覽網(wǎng)頁的時候,服務(wù)器會把動態(tài)生成的Unicode內(nèi)容轉(zhuǎn)換為UTF-8再傳輸?shù)綖g覽器:

image.png

很多網(wǎng)頁的源碼上會有類似<meta charset="UTF-8" />的信息,表示該網(wǎng)頁正是用的UTF-8編碼。

Python字符串編碼

Python 3版本中,字符串是以Unicode編碼的,也就是說,Python的字符串支持多語言:

print('浪子大俠,你好!')
浪子大俠,你好

對于單個字符的編碼,Python提供了ord()函數(shù)獲取字符的整數(shù)表示,chr()函數(shù)把編碼轉(zhuǎn)換為對應(yīng)的字符:

>>> ord('A')
65
>>> ord('浪')
28010
>>> chr(66)
'B'
>>> chr(20384)
'俠'

如果知道字符的整數(shù)編碼,還可以用十六進(jìn)制這么寫str:

>>> ord('浪')
28010
>>> hex(28010)
'0x6d6a'
>>> ord('子')
23376
>>> hex(23376)
'0x5b50'
>>> ord('大')
22823
>>> hex(22823)
'0x5927'
>>> ord('俠')
20384
>>> hex(20384)
'0x4fa0'
>>> '\u6d6a\u5b50\u5927\u4fa0'
'浪子大俠'

由于Python的字符串類型是str,在內(nèi)存中以Unicode表示,一個字符對應(yīng)若干個字節(jié)。如果要在網(wǎng)絡(luò)上傳輸,或者保存到磁盤上,就需要把str變?yōu)橐宰止?jié)為單位的bytes。

Python對bytes類型的數(shù)據(jù)用帶b前綴的單引號或雙引號表示:

x = b'ABC'

要注意區(qū)分'ABC'和b'ABC',前者是str,后者雖然內(nèi)容顯示得和前者一樣,但bytes的每個字符都只占用一個字節(jié)。
以Unicode表示的str通過encode()方法可以編碼為指定的bytes,例如:

>>> 'ABC'.encode('ascii')
b'ABC'
>>> '浪子大俠'.encode('utf-8')
b'\xe6\xb5\xaa\xe5\xad\x90\xe5\xa4\xa7\xe4\xbe\xa0'
>>> '浪子大俠'.encode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

純英文的str可以用ASCII編碼為bytes,內(nèi)容是一樣的,含有中文的str可以用UTF-8編碼為bytes。含有中文的str無法用ASCII編碼,因為中文編碼的范圍超過了ASCII編碼的范圍,Python會報錯
在bytes中,無法顯示為ASCII字符的字節(jié),用\x##顯示(二進(jìn)制的十六進(jìn)制表示)。

\x## 怎么來的呢
根據(jù)UTF-8 的編碼規(guī)則確認(rèn)二進(jìn)制,再轉(zhuǎn)十六進(jìn)制,就明白了!

>>> ord('浪')
28010
>>> hex(28010)
'0x6d6a'
>>>bin(0x6d6a)
'0b110110101101010'

1110xxxx 10xxxxxx 10xxxxxx
'0b 110 110101 101010'
11100110 10110101 10101010

>>> ord('子')
23376
>>> hex(23376)
'0x5b50'
>>>bin(0x5b50)
'0b101101101010000'

1110xxxx 10xxxxxx 10xxxxxx
'0b 101 101101 010000'
11100101 10101101 10010000

>>> ord('大')
22823
>>> hex(22823)
'0x5927'
>>>bin(0x5927)
'0b101100100100111'

1110xxxx 10xxxxxx 10xxxxxx
'0b101 100100 100111'
11100101 10100100 10100111

>>> ord('俠')
20384
>>> hex(20384)
'0x4fa0'
>>>bin(0x4fa0)
'0b100111110100000'

1110xxxx 10xxxxxx 10xxxxxx
'0b100 111110 100000'
11100100 10111110 10100000

>>>hex(0b111001101011010110101010111001011010110110010000111001011010010010100111111001001011111010100000)
'0xe6b5aae5ad90e5a4a7e4bea0'

'0xe6 b5 aa e5 ad 90 e5 a4 a7 e4 be a0'
b'\xe6\xb5\xaa\xe5\xad\x90\xe5\xa4\xa7\xe4\xbe\xa0'

反過來,如果我們從網(wǎng)絡(luò)或磁盤上讀取了字節(jié)流,那么讀到的數(shù)據(jù)就是bytes。要把bytes變?yōu)閟tr,就需要用decode()方法:

>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe6\xb5\xaa\xe5\xad\x90\xe5\xa4\xa7\xe4\xbe\xa0'.decode('utf-8')
'浪子大俠'

如果bytes中包含無法解碼的字節(jié),decode()方法會報錯:

>>> b'\xe6\xb5\xaa\xe5\xad\x90\xe5\xa4\xa7\xe4\xbe\xff'.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 9-10: invalid continuation byte

如果bytes中只有一小部分無效的字節(jié),可以傳入errors='ignore'忽略錯誤的字節(jié):

>>> b'\xe6\xb5\xaa\xe5\xad\x90\xe5\xa4\xa7\xe4\xbe\xff'.decode('utf-8',errors='ignore')
'浪子大'

要計算str包含多少個字符,可以用len()函數(shù):

>>> len('ABC')
3
>>> len('浪子大俠')
4

len()函數(shù)計算的是str的字符數(shù),如果換成bytes,len()函數(shù)就計算字節(jié)數(shù)

>>> len(b'ABC')
3
>>> len(b'\xe6\xb5\xaa\xe5\xad\x90\xe5\xa4\xa7\xe4\xbe\xa0')
12
>>> len('浪子大俠'.encode('utf-8'))
12

可見,1個中文字符經(jīng)過UTF-8編碼后通常會占用3個字節(jié),而1個英文字符只占用1個字節(jié)。

在操作字符串時,我們經(jīng)常遇到str和bytes的互相轉(zhuǎn)換。為了避免亂碼問題,應(yīng)當(dāng)始終堅持使用UTF-8編碼對str和bytes進(jìn)行轉(zhuǎn)換。

由于Python源代碼也是一個文本文件,所以,當(dāng)你的源代碼中包含中文的時候,在保存源代碼時,就需要務(wù)必指定保存為UTF-8編碼。當(dāng)Python解釋器讀取源代碼時,為了讓它按UTF-8編碼讀取,我們通常在文件開頭寫上這兩行:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

第一行注釋是為了告訴Linux/OS X系統(tǒng),這是一個Python可執(zhí)行程序,Windows系統(tǒng)會忽略這個注釋;
第二行注釋是為了告訴Python解釋器,按照UTF-8編碼讀取源代碼,否則,你在源代碼中寫的中文輸出可能會有亂碼。
申明了UTF-8編碼并不意味著你的.py文件就是UTF-8編碼的,必須并且要確保文本編輯器正在使用UTF-8 without BOM編碼:

Python字符串格式化

格式化字符串字面值
內(nèi)嵌表達(dá)式的字符串字面值。
格式字符串語法
使用 str.format() 進(jìn)行字符串格式化。
printf 風(fēng)格的字符串格式化
這里詳述了使用 % 運算符進(jìn)行字符串格式化。

格式化字符串字面值

格式化字符串字面值 或稱 f-string 是帶有 'f' 或 'F' 前綴的字符串字面值。這種字符串可包含替換字段,即以 {} 標(biāo)示的表達(dá)式。而其他字符串字面值總是一個常量,格式化字符串字面值實際上是會在運行時被求值的表達(dá)式。

字符串在花括號以外的部分按其字面值處理,除了雙重花括號 '{{' 或 '}}' 會被替換為相應(yīng)的單個花括號。單個左花括號 '{' 標(biāo)示一個替換字段,它以一個 Python 表達(dá)式打頭,表達(dá)式之后可能有一個以嘆號 '!' 標(biāo)示的轉(zhuǎn)換字段。之后還可能帶有一個以冒號 ':' 標(biāo)示的格式說明符。替換字段以一個右花括號 '}' 作為結(jié)束。

格式化字符串字面值中的表達(dá)式會被當(dāng)作包含在圓括號中的普通 Python 表達(dá)式一樣處理,但有少數(shù)例外。 空表達(dá)式不被允許,lambda 和賦值表達(dá)式 := 必須顯式地加上圓括號。 替換表達(dá)式可以包含換行(例如在三重引號字符串中),但是不能包含注釋。 每個表達(dá)式會在格式化字符串字面值所包含的位置按照從左至右的順序被求值。

如果指定了轉(zhuǎn)換符,表達(dá)式的求值結(jié)果會先轉(zhuǎn)換再格式化。
轉(zhuǎn)換符 '!s' 即對結(jié)果調(diào)用 str()
'!r' 為調(diào)用 repr()
'!a' 為調(diào)用 ascii()

在此之后結(jié)果會使用 format() 協(xié)議進(jìn)行格式化。格式說明符會被傳入表達(dá)式或轉(zhuǎn)換結(jié)果的 __format__() 方法。如果省略格式說明符則會傳入一個空字符串。然后格式化結(jié)果會包含在整個字符串最終的值當(dāng)中。

頂層的格式說明符可以包含有嵌套的替換字段。這些嵌套字段也可以包含有自己的轉(zhuǎn)換字段和 格式說明符,但不可再包含更深層嵌套的替換字段。這里的 格式說明符微型語言 與字符串 .format() 方法所使用的相同。

格式化字符串字面值可以拼接,但是一個替換字段不能拆分到多個字面值。

示例:

name = "Denny"
print(f"He said his name is {name!r}")
print(f"He said his name is {repr(name)}")
執(zhí)行結(jié)果:
He said his name is 'Denny'
He said his name is 'Denny'

import decimal
width = 10
precision = 4
value = decimal.Decimal("12.34567")
print(f"result: {value:{width}.{precision}}") # ?width的含義width 是一個定義最小總字段寬度的十進(jìn)制整數(shù),包括任何前綴、分隔符和其他格式化字符。 如果未指定,則字段寬度將由內(nèi)容確定。
執(zhí)行結(jié)果:
result:      12.35

from datetime import datetime
today = datetime(year=2020, month=3, day=14)
print(f"{today:%B %d, %Y}")
執(zhí)行結(jié)果:
March 14, 2020

number = 1024
print(f"{number:#0x}")
print(f"{number:#0o}")
print(f"{number:#0b}")
執(zhí)行結(jié)果:
0x400
0o2000
0b10000000000

與正常字符串字面值采用相同語法導(dǎo)致的一個結(jié)果就是替換字段中的字符不能與外部的格式化字符串字面值所用的引號相沖突:
內(nèi)外需要使用不同的引號

age = {
    'Denny':23,
    'Sunny':24,
    'Paul':25
}
# print(f"Denny age is {age["Denny"]}") # SyntaxError: invalid syntax
print(f"Denny age is {age['Denny']}")
print(f'Sunny age is {age["Sunny"]}')
執(zhí)行結(jié)果:
Denny age is 23
Sunny age is 24

格式表達(dá)式中不允許有反斜杠,這會引發(fā)錯誤:

print(f"newline: {ord('\n')}")
執(zhí)行結(jié)果:
SyntaxError: f-string expression part cannot include a backslash

newline = ord('\n')
print(f"newsline: {newline}")
執(zhí)行結(jié)果:
newsline: 10

格式化字符串字面值不可用作文檔字符串,即便其中沒有包含表達(dá)式。

>>> def test():
...     '''test ok'''
... 
>>> test.__doc__
'test ok

>>> def foo():
...     f"Not a docstring"
...
>>> foo.__doc__ is None
True

格式字符串語法

str.format() 方法和 Formatter 類共享相同的格式字符串語法(雖然對于 Formatter 來說,其子類可以定義它們自己的格式字符串語法)。 具體語法與格式化字符串字面值相似,但也存在區(qū)別。

格式字符串包含有以花括號 {} 括起來的“替換字段”。 不在花括號之內(nèi)的內(nèi)容被視為字面文本,會不加修改地復(fù)制到輸出中。 如果你需要在字面文本中包含花括號字符,可以通過重復(fù)來轉(zhuǎn)義: {{ and }}。

用不太正式的術(shù)語來描述,替換字段開頭可以用一個 field_name 指定要對值進(jìn)行格式化并取代替換字符被插入到輸出結(jié)果的對象。 field_name 之后有可選的 conversion 字段,它是一個感嘆號 '!' 。加一個 format_spec,并以一個冒號 ':' 打頭。 這些指明了替換值的非默認(rèn)格式。

field_name 本身以一個數(shù)字或關(guān)鍵字 arg_name 打頭。 如果為數(shù)字,則它指向一個位置參數(shù),而如果為關(guān)鍵字,則它指向一個命名關(guān)鍵字參數(shù)。 如果格式字符串中的數(shù)字 arg_names 為 0, 1, 2, ... 的序列,它們可以全部省略(而非部分省略),數(shù)字 0, 1, 2, ... 將會按順序自動插入。 由于 arg_name 不使用引號分隔,因此無法在格式字符串中指定任意的字典鍵 (例如字符串 '10' 或 ':-]') arg_name 之后可以帶上任意數(shù)量的索引或?qū)傩员磉_(dá)式。 '.name' 形式的表達(dá)式會使用 getattr() 選擇命名屬性,而 '[index]' 形式的表達(dá)式會使用 __getitem__() 執(zhí)行索引查找。
一些簡單的格式字符串示例

"First, thou shalt count to {0}"  # References first positional argument
"Bring me a {}"                   # Implicitly references the first positional argument
"From {} to {}"                   # Same as "From {0} to {1}"
"My quest is {name}"              # References keyword argument 'name'
"Weight in tons {0.weight}"       # 'weight' attribute of first positional arg
"Units destroyed: {players[0]}"   # First element of keyword argument 'players'.

使用 conversion 字段在格式化之前進(jìn)行類型強制轉(zhuǎn)換。 通常,格式化值的工作由值本身的 __format__() 方法來完成。 但是,在某些情況下最好強制將類型格式化為一個字符串,覆蓋其本身的格式化定義。 通過在調(diào)用 __format__() 之前將值轉(zhuǎn)換為字符串,可以繞過正常的格式化邏輯。

目前支持的轉(zhuǎn)換旗標(biāo)有三種: '!s' 會對值調(diào)用 str()'!r' 調(diào)用 repr()'!a' 則調(diào)用 ascii()

"Harold's a clever {0!s}"        # Calls str() on the argument first
"Bring out the holy {name!r}"    # Calls repr() on the argument first
"More {!a}"                      # Calls ascii() on the argument first

format_spec 字段包含值應(yīng)如何呈現(xiàn)的規(guī)格描述,例如字段寬度、對齊、填充、小數(shù)精度等細(xì)節(jié)信息。 每種值類型可以定義自己的“格式化迷你語言”或?qū)?format_spec 的解讀方式。

大多數(shù)內(nèi)置類型都支持同樣的格式化迷你語言

format_spec 字段還可以在其內(nèi)部包含嵌套的替換字段。 這些嵌套的替換字段可能包括字段名稱、轉(zhuǎn)換旗標(biāo)和格式規(guī)格描述,但是不再允許更深層的嵌套。 format_spec 內(nèi)部的替換字段會在解讀 format_spec 字符串之前先被解讀。 這將允許動態(tài)地指定特定值的格式。

格式規(guī)格迷你語言

格式示例

本節(jié)包含 str.format() 語法的示例以及與舊式 % 格式化的比較。

該語法在大多數(shù)情況下與舊式的 % 格式化類似,只是增加了 {}: 來取代 %。 例如,,'%03.2f' 可以被改寫為 '{:03.2f}'

新的格式語法還支持新增的不同選項,將在以下示例中說明。

  1. 按位置訪問參數(shù):
>>> '{0}, {1}, {2}'.format('a', 'b', 'c')
'a, b, c'
>>> '{}, {}, {}'.format('a', 'b', 'c')  # 3.1+ only
'a, b, c'
>>> '{2}, {1}, {0}'.format('a', 'b', 'c')
'c, b, a'
>>> '{2}, {1}, {0}'.format(*'abc')      # 解包字符串系列
'c, b, a'
>>> '{0}{1}{0}'.format('abra', 'cad')   # 參數(shù)的索引可以重復(fù)
'abracadabra'
  1. 按名稱訪問參數(shù):
>>> 'Sunny: {sex}, {age}'.format(sex='男', age=23)
Sunny: 男, 23
>>> sunny = {'sex': '男', 'age': 23}
>>> 'Sunny: {sex}, {age}'.format(**sunny)
Sunny: 男, 23
  1. 訪問參數(shù)的屬性:
c = 3-5j
print('The complex number {0} is formed from the real part {0.real}\
 and the imaginary part {0.imag}.'.format(c))
執(zhí)行結(jié)果:
The complex number (3-5j) is formed from the real part 3.0 and the imaginary part -5.0.
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __str__(self):
        return 'Point({self.x}, {self.y})'.format(self=self)

print(Point(4, 2))
執(zhí)行結(jié)果:
Point(4, 2)
  1. 訪問參數(shù)的項:
coord = (3,5)
print('X:{0[0]}; Y:{0[1]}'.format(coord))
執(zhí)行結(jié)果:
X:3; Y:5
  1. 替代 %s 和 %r:
>>> "repr() shows quotes: {!r}; str() doesn't: {!s}".format('test1', 'test2')
"repr() shows quotes: 'test1'; str() doesn't: test2"
  1. 對齊文本以及指定寬度:
>>> '{:<30}'.format('left aligned')
'left aligned                  '
>>> '{:>30}'.format('right aligned')
'                 right aligned'
>>> '{:^30}'.format('centered')
'           centered           '
>>> '{:*^30}'.format('centered')  # use '*' as a fill char
'***********centered***********'
  1. 替代 %+f, %-f 和 % f 以及指定正負(fù)號:
>>> '{:+f}; {:+f}'.format(3.14, -3.14)  # 總是顯示符號位
'+3.140000; -3.140000'
>>> '{: f}; {: f}'.format(3.14, -3.14)  # 正數(shù)前顯示空格
' 3.140000; -3.140000'
>>> '{:-f}; {:-f}'.format(3.14, -3.14)  # 只顯示負(fù)數(shù)的符號 同 '{:f}; {:f}'
'3.140000; -3.140000'
  1. 替代 %x 和 %o 以及轉(zhuǎn)換基于不同進(jìn)位制的值:
>>> # format also supports binary numbers
>>> "int: {0:d};  hex: {0:x};  oct: {0:o};  bin: {0:b}".format(42)
'int: 42;  hex: 2a;  oct: 52;  bin: 101010'
>>> # with 0x, 0o, or 0b as prefix:
>>> "int: {0:d};  hex: {0:#x};  oct: {0:#o};  bin: {0:#b}".format(42)
'int: 42;  hex: 0x2a;  oct: 0o52;  bin: 0b101010'
  1. 使用逗號作為千位分隔符:
>>> '{:,}'.format(1234567890)
'1,234,567,890'
  1. 表示為百分?jǐn)?shù):
>>> points = 19
>>> total = 22
>>> 'Correct answers: {:.2%}'.format(points/total)
'Correct answers: 86.36%'
  1. 使用特定類型的專屬格式化:
>>> import datetime
>>> d = datetime.datetime(2020, 3,14, 2, 11, 58)
>>> '{:%Y-%m-%d %H:%M:%S}'.format(d)
'2020-03-14 02:11:58'
  1. 嵌套參數(shù)以及更復(fù)雜的示例:
>>> for align, text in zip('<^>', ['left', 'center', 'right']):
...     '{0:{fill}{align}16}'.format(text, fill=align, align=align)
...
'left<<<<<<<<<<<<'
'^^^^^center^^^^^'
'>>>>>>>>>>>right'
>>>
>>> octets = [192, 168, 0, 1]
>>> '{:02X}{:02X}{:02X}{:02X}'.format(*octets)
'C0A80001'
>>> int(_, 16) # 在python交互模式下,_符號是指交互解釋器中最后一次執(zhí)行語句的返回結(jié)果。發(fā)現(xiàn)print無效
3232235521
>>>
>>> width = 5
>>> for num in range(5,12): 
...     for base in 'dXob':
...         print('{0:{width}{base}}'.format(num, base=base, width=width), end=' ')
...     print()
...
    5     5     5   101
    6     6     6   110
    7     7     7   111
    8     8    10  1000
    9     9    11  1001
   10     A    12  1010
   11     B    13  1011

printf 風(fēng)格的字符串格式化 (并不推薦使用)

此處介紹的格式化操作具有多種怪異特性,可能導(dǎo)致許多常見錯誤(例如無法正確顯示元組和字典)。 使用較新的 格式化字符串字面值str.format() 接口或 模板字符串 有助于避免這樣的錯誤。 這些替代方案中的每一種都更好地權(quán)衡并提供了簡單、靈活以及可擴展性優(yōu)勢。

字符串具有一種特殊的內(nèi)置操作:使用 % (取模) 運算符。 這也被稱為字符串的 格式化 或 插值 運算符。 對于 format % values (其中 format 為一個字符串),在 format 中的 % 轉(zhuǎn)換標(biāo)記符將被替換為零個或多個 values 條目。 其效果類似于在 C 語言中使用 sprintf()。

如果 format 要求一個單獨參數(shù),則 values 可以為一個非元組對象。否則的話,values 必須或者是一個包含項數(shù)與格式字符串中指定的轉(zhuǎn)換符項數(shù)相同的元組,或者是一個單獨映射對象(例如字典)。

轉(zhuǎn)換標(biāo)記符包含兩個或更多字符并具有以下組成,且必須遵循此處規(guī)定的順序:

  1. '%' 字符,用于標(biāo)記轉(zhuǎn)換符的起始。
  2. 映射鍵(可選),由加圓括號的字符序列組成 (例如 (somename))。
  3. 轉(zhuǎn)換旗標(biāo)(可選),用于影響某些轉(zhuǎn)換類型的結(jié)果。
  4. 最小字段寬度(可選)。 如果指定為 '*' (星號),則實際寬度會從 values 元組的下一元素中讀取,要轉(zhuǎn)換的對象則為最小字段寬度和可選的精度之后的元素。
  5. 精度(可選),以在 '.' (點號) 之后加精度值的形式給出。 如果指定為 '*' (星號),則實際精度會從 values 元組的下一元素中讀取,要轉(zhuǎn)換的對象則為精度之后的元素。
  6. 長度修飾符(可選)。
  7. 轉(zhuǎn)換類型。

當(dāng)右邊的參數(shù)為一個字典(或其他映射類型)時,字符串中的格式必須包含加圓括號的映射鍵,對應(yīng) '%' 字符之后字典中的每一項。 映射鍵將從映射中選取要格式化的值。 例如:

>>>
print('%(language)s has %(number)03d quote types.' %
      {'language': "Python", "number": 2})
Python has 002 quote types.
# 在此情況下格式中不能出現(xiàn) * 標(biāo)記符(因其需要一個序列類的參數(shù)列表)。
標(biāo)志 含義
'#' 值的轉(zhuǎn)換將使用“替代形式”(具體定義見下文)。
'0' 轉(zhuǎn)換將為數(shù)字值填充零字符。
'-' 轉(zhuǎn)換值將靠左對齊(如果同時給出 '0' 轉(zhuǎn)換,則會覆蓋后者)。
' ' (空格) 符號位轉(zhuǎn)換產(chǎn)生的正數(shù)(或空字符串)前將留出一個空格。
'+' 符號字符 ('+' 或 '-') 將顯示于轉(zhuǎn)換結(jié)果的開頭(會覆蓋 "空格" 旗標(biāo))。

可以給出長度修飾符 (h, l 或 L),但會被忽略,因為對 Python 來說沒有必要 -- 所以 %ld 等價于 %d。

轉(zhuǎn)換類型為:

轉(zhuǎn)換符 含義 注釋
'd' 有符號十進(jìn)制整數(shù)。
'i' 有符號十進(jìn)制整數(shù)。
'o' 有符號八進(jìn)制數(shù)。 (1)
'u' 過時類型 -- 等價于 'd' (6)
'x' 有符號十六進(jìn)制數(shù)(小寫)。 (2)
'X' 有符號十六進(jìn)制數(shù)(大寫)。 (2)
'e' 浮點指數(shù)格式(小寫)。 (3)
'E' 浮點指數(shù)格式(大寫)。 (3)
'f' 浮點十進(jìn)制格式。 (3)
'F' 浮點十進(jìn)制格式。 (3)
'g' 浮點格式。 如果指數(shù)小于 -4 或不小于精度則使用小寫指數(shù)格式,否則使用十進(jìn)制格式。 (4)
'G' 浮點格式。 如果指數(shù)小于 -4 或不小于精度則使用大寫指數(shù)格式,否則使用十進(jìn)制格式。 (4)
'c' 單個字符(接受整數(shù)或單個字符的字符串)。
'r' 字符串(使用 repr() 轉(zhuǎn)換任何 Python 對象)。 (5)
's' 字符串(使用 str() 轉(zhuǎn)換任何 Python 對象)。 (5)
'a' 字符串(使用 ascii() 轉(zhuǎn)換任何 Python 對象)。 (5)
'%' 不轉(zhuǎn)換參數(shù),在結(jié)果中輸出一個 '%' 字符。

注釋:

  1. 此替代形式會在第一個數(shù)碼之前插入標(biāo)示八進(jìn)制數(shù)的前綴 ('0o')。
  2. 此替代形式會在第一個數(shù)碼之前插入 '0x''0X' 前綴(取決于是使用 'x' 還是 'X' 格式)。
  3. 此替代形式總是會在結(jié)果中包含一個小數(shù)點,即使其后并沒有數(shù)碼。
    小數(shù)點后的數(shù)碼位數(shù)由精度決定,默認(rèn)為 6。
  4. 此替代形式總是會在結(jié)果中包含一個小數(shù)點,末尾各位的零不會如其他情況下那樣被移除。
    小數(shù)點前后的有效數(shù)碼位數(shù)由精度決定,默認(rèn)為 6。
  5. 如果精度為 N,輸出將截短為 N 個字符。
  6. 參見 PEP 237
    由于 Python 字符串顯式指明長度,%s 轉(zhuǎn)換不會將 '\0' 視為字符串的結(jié)束。

參考:
廖雪峰Python字符串和編碼
阮一峰_字符編碼筆記:ASCII,Unicode 和 UTF-8
Python官方文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內(nèi)容