Python 字串

Python 內建名為 "str" 的字串類別,提供許多便利的功能 (有個 "string" 的舊模組不應該使用)。字串常值可以用雙引號或單引號括住,但單引號較為常見。在單引號和雙引號的文字中,反斜線逸出會照常運作,例如: \n \ \"。雙引號的字串常值可以包含不含任何引號 (例如「我做錯」) 的單引號,同樣地的單引號字串可以含有雙引號。字串常值可以橫跨多行,但每一行的結尾都必須有反斜線「\」,以逸出新行。三引號內的字串文字「"」或「'」可以涵蓋多行文字。

Python 字串「不可變動」,代表這些字串在建立後即無法變更 (Java 字串也會使用這個不可變動的樣式)。由於字串無法變更,因此我們會建構「新」字串,以表示運算值。舉例來說,運算式 ('hello' + 'the'') 會擷取 2 個字串「hello」和「the」,然後建構一個新字串「hellothere」。

字串中的字元可透過標準 [ ] 語法存取,就像 Java 和 C++ 一樣,Python 也會使用從零開始的索引,因此如果 s 是「hello' s[1]」是「e」,如果索引超出字串的邊界,Python 就會引發錯誤。如果 Python 樣式 (與 Perl 不同) 無法判斷如何執行動作,便會暫停 Python 樣式,而不只是單純設定預設值。下方的實用的「slice」語法也能用來擷取字串中的任何子字串。len(string) 函式會傳回字串的長度。[ ] 語法和 len() 函式實際上可用於任何序列類型:字串、清單等。Python 會嘗試讓作業在不同類型中都能以一致的方式運作。Python 新手適用的程式碼:請勿使用「len」做為變數名稱,以免封鎖 len() 函式。「+」運算子可以串連兩個字串。請注意,在下列程式碼中,系統不會預先宣告變數,只要指派給這些變數即可。

  s = 'hi'
  print(s[1])          ## i
  print(len(s))        ## 2
  print(s + ' there')  ## hi there

與 Java 不同,「+」不會自動將數字或其他型別轉換為字串格式。str() 函式會將值轉換為字串格式,以便與其他字串結合。

  pi = 3.14
  ##text = 'The value of pi is ' + pi      ## NO, does not work
  text = 'The value of pi is '  + str(pi)  ## yes

如為數字,標準運算子 +、/、* 就會照常運作。沒有 ++ 運算子,但 += 和 -= 等運算子也有效。若要計算整數除法,請使用 2 個斜線,例如 6 // 5 為 1

「print」函式通常會輸出一或多個 Python 項目,後面接著換行。「raw」字串常值的前置字串是「r」,會將所有字元傳遞到不使用特殊處理反斜線的方式,因此 r'x\nx' 評估為長度 4 字串 'x\nx'。「print」可能會使用多個引數來變更輸出內容的方式 (請參閱 Python.org 列印函式定義),例如將「end」設為「"」,避免在完成列印所有項目後停止輸出換行符號。

  raw = r'this\t\n and that'

  # this\t\n and that
  print(raw)

  multi = """It was the best of times.
  It was the worst of times."""

  # It was the best of times.
  #   It was the worst of times.
  print(multi)

字串方法

以下列舉一些最常見的字串方法。方法與函式類似,但會在物件「上」執行。如果變數 s 是字串,則 s.lower() 程式碼會對該字串物件執行 low() 方法並傳回結果 (這樣在物件上執行方法,就是組成物件導向程式設計 (OOP) 的基本概念之一)。以下列舉幾個最常見的字串方法:

  • s.lower(), s.upper() -- 會傳回字串的小寫或大寫版本
  • s.strip() -- 會傳回從開頭和結尾移除空白字元的字串
  • s.isalpha()/s.isdigit()/s.isspace()... -- 測試所有字串字元是否屬於各種字元類別
  • s.startswith('other'), s.endswith('other') -- 測試字串的開頭或結尾是否為指定的其他字串
  • s.find('other') -- 搜尋 s 中的指定其他字串 (非規則運算式),然後傳回第一個開始的索引 (如果找不到,則傳回 -1)
  • s.replace('old', 'new') -- 會傳回字串,其中所有出現的「old」都已替換為「new」
  • s.Split('delim') -- 會傳回以指定分隔符號分隔的子字串清單。分隔符號不是規則運算式,而是文字。'aaa,bbb,ccc'.split(',') -> ['aaa', 'bbb', 'ccc']。為了方便起見,s.split() (不含引數) 會在所有空白字元上分割。
  • s.join(list) -- 對 split() 的反面,使用字串做為分隔符號,將指定清單中的元素彙整在一起,例如「---'.join(['aaa', 'bbb', 'ccc']) -> aaa---bbb---ccc

使用 Google 搜尋「Python str」後,系統會將您導向正式的 python.org 字串方法,其中會列出所有 str 方法。

Python 沒有獨立的字元類型。反之,例如 s[8] 的運算式會傳回包含該字元的字串-length-1。透過該 string-length-1,運算子 ==、<=、... 全都會照常運作,因此大部分情況下,您不需要瞭解 Python 沒有獨立的純量「char」類型。

字串配量

想要參照序列的子部分,通常是字串和清單,而「切片」語法是相當實用的方法。配量 [start:end] 是指元素的開頭和延伸,不會包含結尾元素。假設我們有 =「Hello」

包含字母的「hello」字串索引值:0 1 2 3 4

  • s[1:4] 為「ell」 -- 從索引 1 開始的字元,擴充為不含索引 4 的字元
  • s[1:] 為「ello」,省略任一索引預設為字串的開頭或結尾
  • s[:] 是「Hello」 -- 省略兩者一律會提供給我們完整內容的副本 (這是將序列複製如字串或清單的 Python 方法)
  • s[1:100] 是「ello」 -- 過大的索引會截斷至字串長度

標準的索引號碼從零開始,可讓您輕鬆存取接近字串開頭的字元。或者,Python 使用負數來輕鬆存取字串結尾的字元:s[-1] 是最後一個字元「o」,s[-2] 是「l」到下一個字元,依此類推。從字串尾端起算的負索引數:

  • s[-1] 是「o」 -- 最後一個字元 (從最後 1 個字元開始)
  • s[-4] 是「e」 -- 第 4 次
  • s[:-3] 是「他」,最後 3 個字元,最多不包含最後 3 個字元。
  • s[-3:] 是「llo」,從結尾的第 3 個字元開始,延伸至字串結尾。

這是針對任何索引 n (s[:n] + s[n:] == s) 的切片細膩。這也適用於 n 負數或邊界。或者,其他方式 s[:n] 和 s[n:] 一律會將字串分區為兩個字串部分,並保留所有字元。您將在清單部分看到,切片也能與清單搭配使用。

字串格式

Python 可採取的其中一個簡單做法,就是自動將物件轉換為適合列印的字串。有兩個內建方法可以做到這一點,也就是格式化字串常值 (也稱為「f 字串」) 和叫用 str.format()。

格式化字串常值

您經常會在下列情況中看到格式化字串常值:

  value = 2.791514
  print(f'approximate value = {value:.2f}')  # approximate value = 2.79

  car = {'tires':4, 'doors':2}
  print(f'car = {car}') # car = {'tires': 4, 'doors': 2}

格式化常值字串的前置字串是「f」(例如原始字串使用的「r」前置字串)。大括號「{}」外的任何文字都會直接輸出。「{}」中包含的運算式是以格式規格所述的格式規格列印出來。格式規格中有許多繁瑣的功能,包括截斷和轉換成科學記號,以及靠左/右/置中對齊。

如果您想輸出物件表格,並且想要代表不同物件屬性的資料欄對齊方式,f 字串就很實用

  address_book = [{'name':'N.X.', 'addr':'15 Jones St', 'bonus': 70},
      {'name':'J.P.', 'addr':'1005 5th St', 'bonus': 400},
      {'name':'A.A.', 'addr':'200001 Bdwy', 'bonus': 5},]

  for person in address_book:
    print(f'{person["name"]:8} || {person["addr"]:20} || {person["bonus"]:>5}')

  # N.X.     || 15 Jones St          ||    70
  # J.P.     || 1005 5th St          ||   400
  # A.A.     || 200001 Bdwy          ||     5

字串百分比

Python 也具有類似 printf() 的舊版功能,可組合字串。% 運算子會接受左側的 printf 類型格式字串 (%d int、%s 字串、%f/%g 浮點),以及右側元組中的相符值 (元組是由以半形逗號分隔的值組成,通常在括號中組成):

  # % operator
  text = "%d little pigs come out, or I'll %s, and I'll %s, and I'll blow your %s down." % (3, 'huff', 'puff', 'house')

上面這一行的長度很長,假設您想要分行將其分割成不同的行。您無法按照在其他語言中的 '%' 之後分隔這一行,因為根據預設,Python 會將每一行視為個別的陳述式 (因此在加號上,就不需要在每一行輸入分號)。若要修正此問題,請將整個運算式包在一組外括號內,這樣運算式即可橫跨多行。這項跨行程式碼技巧適用於下列各種分組結構:( )、[ ]、{ }。

  # Add parentheses to make the long line work:
  text = (
    "%d little pigs come out, or I'll %s, and I'll %s, and I'll blow your %s down."
    % (3, 'huff', 'puff', 'house'))

這樣好了,但線條還是很長。Python 能讓您將一行分割為多個片段,然後進一步自動串連。因此,為了讓這行程式碼更短,我們可以這樣做:

  # Split the line into chunks, which are concatenated automatically by Python
  text = (
    "%d little pigs come out, "
    "or I'll %s, and I'll %s, "
    "and I'll blow your %s down."
    % (3, 'huff', 'puff', 'house'))

字串 (萬國碼 (Unicode) 與位元組)

一般 Python 字串是 Unicode。

Python 也支援由純位元組組成的字串 (在字串常值前方以「b」符號表示),例如:

> byte_string = b'A byte string'
> byte_string
  b'A byte string'

萬國碼 (Unicode) 字串是位元組字串中的不同物件類型,但只要傳遞任一類型字串,各種程式庫 (例如規則運算式) 即可正常運作。

如要將一般 Python 字串轉換為位元組,請對字串呼叫 Encode() 方法。順帶一提,位元組字串 decode() 方法會將已編碼的純位元組轉換為萬國碼 (Unicode) 字串:

> ustring = 'A unicode \u018e string \xf1'
> b = ustring.encode('utf-8')
> b
b'A unicode \xc6\x8e string \xc3\xb1'  ## bytes of utf-8 encoding. Note the b-prefix.
> t = b.decode('utf-8')                ## Convert bytes back to a unicode string
> t == ustring                         ## It's the same as the original, yay!

True

在檔案閱讀部分中,示範的是如何開啟含有某些編碼的文字檔,以及讀取萬國碼 (Unicode) 字串。

If 陳述式

Python 不會使用 { } 來包住 if/loops/function 的程式碼區塊。而是改用冒號 (:) 和縮排/空白字元將陳述式分組。if 的布林值測試不需要在括號中 (與 C++/Java 有大差異),並且可以包含 *elif* 和 *else* 子句 (記憶:「elif」一詞的長度與「else」這個字的長度相同)。

任何值都可以用做 if-test。所有「零」值都會計為 false:無、0、空白字串、空白清單、空白字典。還有一個包含兩個值的布林值類型:True 和 False (轉換為 int,兩者為 1 和 0)。Python 的比較運算操作如下:==、!=、<、<=、>、>=。與 Java 和 C 不同,== 經過超載,可與字串正確搭配使用。布林運算子為「和」*、「或」*、*not* (Python 不使用 C-style && || !)。以下是程式碼在一天當中可能會提供飲料建議的程式碼外觀。請注意,gan/else 陳述式的每個區塊都以 : 開頭,且陳述式會以縮排的方式分組:

  if time_hour >= 0 and time_hour <= 24:
    print('Suggesting a drink option...')
    if mood == 'sleepy' and time_hour < 10:
      print('coffee')
    elif mood == 'thirsty' or time_hour < 2:
      print('lemonade')
    else:
      print('water')

我發現在上述程式碼類型中輸入時,省略「:」是我最常見的語法錯誤,因為相較於我的 C++/Java 習慣,這會是另一件事。此外,請勿將布林值測試加上括號,也就是 C/Java 的習慣。如果程式碼很短,您可以將程式碼放在同一行的「:」之後,例如:函式、迴圈等 (這也適用於函式、迴圈等),雖然有些人認為分隔不同行的內容比較容易理解。

  if time_hour < 10: print('coffee')
  else: print('water')

運動:string1.py

如要練習本節所述的內容,請嘗試透過基本練習中的 string1.py 練習。