💡 先搞懂問題
晴空咖啡的訂單編號長這樣:"TPE-2026-A"。前三碼是門市、中間四碼是年份、最後一碼是會員等級。店長想寫程式把年份取出來,卻不知道怎麼「只拿一部分」。他還遇到兩件怪事:為了統一格式寫了 name.upper(),印出 name 卻還是小寫;想印「小計 136.50 元」,用第 3 課的 + 和 str() 拼了一長串,印出來卻是 136.5,少了一個 0。
這些都是字串(string,str)的問題。第 3 課說過,字串是用引號包起來的文字;這一課要更進一步:字串其實是一串字元(character,單一個字母、數字、中文字或符號)排成一列,每個字元都有編號。知道編號,就能取出任何一個字或任何一段;再加上字串自帶的「方法」和 f-string,大部分的文字整理工作都做得到。
生活比喻:印在方格紙條上的一行字
想像收銀機印出一張方格紙條,一格印一個字:「T」「P」「E」「-」「2」……。每一格底下有編號,從最左邊的 0 開始。想要第 0 格的字,就看第 0 格;想要年份,就沿著第 4 條和第 8 條格線之間,把那一段影印下來。
這張紙條印好之後就不能塗改。如果想要全部大寫的版本,只能請收銀機照著內容再印一張新的紙條;舊紙條還在原地,誰拿著它,看到的仍然是原本的字。另外,收銀機還有一種「預先印好空格的表單」,像「__ 杯共 __ 元」,把數字填進空格就是一句完整的話。
code[0] 是第一個字;「沿兩條格線影印下中間那段」是切片(slicing),code[4:8] 取出 '2026'。紙條不能塗改,對應字串是不可變的(immutable):code[0] = "K" 會出錯;name.upper() 是「照著內容印一張新的」,不會改到原本的字串,要用 name = name.upper() 把名字改貼到新字串上。預先印好空格的表單,就是 f-string。
比喻有三個地方和實際不同。第一,紙條的編號只有從左數的一組,Python 另外提供一組從右邊倒數的負索引:code[-1] 是最後一個字,code[-2] 是倒數第二個。第二,現實中要店員影印「第 8 到第 20 格」,他會說紙條沒有那麼長;Python 的切片碰到超出範圍的編號,會自動停在結尾,不會出錯,code[8:20] 只會拿到 '-A'。只有用單一索引取一個不存在的格子(例如 code[10])才會出錯。第三,影印下來的那一段是一個新的字串物件,和原紙條沒有連動,改了也互不影響。
索引從 0 開始,也可以從後面數
為什麼從 0 開始?可以把索引理解成「從開頭往右走幾步」:第一個字不用走,所以是 0。這是 Python 和大多數程式語言的共同習慣,第 5 課的串列也一樣。len(s) 回傳字串有幾個字元,中文字一個字也算 1;因為從 0 起算,最後一個字的索引是 len(s) - 1。
🎮 互動實驗室一:用索引和切片拆開訂單編號
按「下一步」一行一行執行,看每一個索引或切片取出了哪個字串。右邊每個綠框都是一個字串物件;從 code 切出來的每一段,都是另外做出來的新字串。右上角的選單可以切換到「索引超出範圍」的版本,看看什麼時候會出錯。遇到 🤔 先猜再按下一步。
code[0] 和第 3 行的 code[-1] 各取出一個字;第 5、6 行的切片取出一段,結尾那個編號的字不包含在內。第 10 行的 code[8:20] 超出長度,結果只是比較短的 '-A'。切到第二個範例,code[9] 還拿得到 A,code[10] 就出現 IndexError:單一索引超出範圍會出錯,切片不會。🎮 互動實驗室二:upper() 之後,name 為什麼沒變
四個版本都從 name = "amy" 和 backup = name 開始,兩個名字貼在同一個字串上;差別只在第 3 行怎麼「改」。用右上角的選單切換,注意右欄的箭頭:哪個版本讓 name 改貼到新的字串,哪個版本什麼都沒變,哪個版本直接出錯。
upper() 確實做出了 'AMY',但沒有任何名字接住它,馬上就被丟掉。② 的第 3 行讓 name 的箭頭改指到新的 'AMY'(箭頭會變色),backup 還指著原本的 'amy',證明舊字串一個字都沒被改。③ 想直接改第 0 格,Python 不允許。④ 用切片 name[1:] 加上新的開頭,組出一個新字串再改貼,這才是「修改字串」的正確做法。🎮 互動實驗室三:切片探索器
先選一個字串,再選模式:「單一索引」取一個字,「切片」取一段。下拉選單可以留空,和 Python 一樣:start 留空代表從頭、stop 留空代表到尾、step 留空代表 1。格子上方是索引、下方是負索引,被選到的格子會浮起來並標上取出的順序;黃色直線是切片的起點與終點格線。下方有五個任務,點一下任務會換成對應的字串,調到正確結果就會打勾。
🎮 互動實驗室四:字串方法與 f-string,12 張預測輸出卡
每張卡是一兩行程式,用到字串方法或 f-string。先選出你認為畫面會印出什麼,再看 Python 3.13 實際的輸出和原因。答完按「下一張」,全部做完會顯示答對幾張;隨時可以按「重來」。
📘 原理補完
正式名稱與官方用語
Python 官方文件把 str 歸類為文字序列型別(text sequence type)。序列(sequence)的意思是「有順序、可以用編號取出元素的一串東西」,第 5 課的串列也是序列,所以索引和切片的規則在串列上完全一樣。Python 沒有單獨的「字元」型別,code[0] 取出來的 'T' 是長度 1 的字串。
寫成 字串.名稱(…) 的東西叫方法(method):專屬於某個型別的函式,用「點」接在物件後面呼叫。字串是不可變的(immutable)序列,所以官方文件介紹 upper()、replace() 這類方法時,用的說法都是「回傳一份副本」(return a copy):原字串不動,結果是新字串。f-string 的正式名稱是格式化字串字面值(formatted string literal),大括號裡冒號後面那段叫格式規格(format specification)。
索引與切片的規則
單一索引 s[i] 取一個字元,i 可以是 0 到 len(s) - 1,或 -1 到 -len(s),超出就是 IndexError。切片 s[start:stop:step] 取一段,三個數字都可以省略:start 省略代表從頭,stop 省略代表到尾,step 省略代表 1。step 是負數時會倒著走,s[::-1] 就是整串反過來。切片的數字超出範圍時,Python 會自動停在開頭或結尾,不會出錯。
| 寫法(s = "Python") | 結果 | 說明 |
|---|---|---|
s[0]、s[5] | 'P'、'n' | 第一個字是索引 0,最後一個是 len(s) − 1 = 5 |
s[-1]、s[-6] | 'n'、'P' | 負索引從右邊倒數 |
s[6] | IndexError | 單一索引超出範圍會出錯 |
s[1:4] | 'yth' | 包含索引 1,不包含索引 4 |
s[:2]、s[2:] | 'Py'、'thon' | 省略 start 從頭、省略 stop 到尾 |
s[-3:] | 'hon' | 最後 3 個字 |
s[::2] | 'Pto' | 每 2 格取一個(索引 0、2、4) |
s[::-1] | 'nohtyP' | step 為負數:倒著走,整串反轉 |
s[4:100]、s[10:] | 'on'、'' | 切片超出範圍不出錯,可能得到空字串 |
字串不能修改:方法都回傳新字串
字串一旦做出來,裡面的字元就不能換。name[0] = "A" 會出現 TypeError;name.upper() 不會報錯,但它是做出一個新的字串交給你,name 還是貼在舊的那個上面。所以呼叫字串方法時,幾乎都要寫成 結果 = 字串.方法(…) 把新字串接住,或直接拿去 print。這和第 2 課的觀念一致:= 是把名字改貼到右邊算出來的物件上。
常用字串方法
| 寫法 | 結果 | 做什麼 |
|---|---|---|
"latte".upper() | 'LATTE' | 全部轉大寫 |
"LATTE".lower() | 'latte' | 全部轉小寫,比對使用者輸入前常先做 |
" Hi ".strip() | 'Hi' | 去掉前後的空白與換行,中間的不動 |
"2026/10/08".replace("/", "-") | '2026-10-08' | 把所有的舊字換成新字 |
"a,b,c".split(",") | ['a', 'b', 'c'] | 依分隔符號切開,得到串列 |
"-".join(["a", "b"]) | 'a-b' | 用前面的符號把多段字串接起來 |
"banana".find("n") | 2 | 第一次出現的索引;找不到回傳 -1 |
"an" in "banana" | True | 有沒有包含這段文字(結果是布林值) |
len("拿鐵") | 2 | 字元數;len 是函式,不是方法,寫法不同 |
f-string:把值填進句子
在字串的引號前面加一個 f,裡面就可以用大括號放變數或運算式,Python 會把結果轉成文字填進去,不必自己寫 str() 和一堆 +。大括號裡加上冒號,可以指定顯示格式:{price:.2f} 固定兩位小數,{n:,} 加上千分位逗號,兩者也能合起來寫成 {x:,.2f},1234.5 會顯示成 1,234.50。
\n 換行與跳脫字元
有些字元沒辦法直接打在引號裡,例如「換行」,或是和外面引號相同的引號。這時用反斜線 \ 開頭的跳脫序列來表示:\n 換行、\t 定位(Tab)、\" 雙引號、\' 單引號、\\ 一個反斜線。另一個辦法是外面改用另一種引號,例如 '她說:"好喝"'。
常見錯誤與錯誤訊息的長相
下面的訊息都是 Python 3.13 實際印出來的(File "main.py" 那一行,在你的電腦上會是完整的檔案路徑)。
Traceback (most recent call last):
File "main.py", line 2, in <module>
msg[0] = "J"
~~~^^^
TypeError: 'str' object does not support item assignment
「str 物件不支援對單一項目指派」。字串不能就地修改,改用 msg = "J" + msg[1:] 做出新字串。Traceback (most recent call last):
File "main.py", line 2, in <module>
print(code[3])
~~~~^^^
IndexError: string index out of range
code = "TPE" 長度是 3,索引只有 0、1、2。要拿最後一個字,寫 code[-1] 比 code[len(code)] 安全。word.upper() 單獨一行,Python 照做、做完就丟,word 不變。這是最常見、也最難發現的錯,要寫成 word = word.upper()。<built-in method upper of str object at 0x7ff4800f1d40>
print(name.upper) 少了 (),印出的是「upper 這個方法本身」,不是結果(後面的 0x… 每次執行都不一樣)。有括號才是「呼叫」。print("{total:.2f}") 印出 {total:.2f},因為少了開頭的 f,它只是一般字串。path = "C:\data\new",Python 3.13 會先警告:
main.py:1: SyntaxWarning: invalid escape sequence '\d' path = "C:\data\new"而且
\n 被當成換行,印出來變成兩行 C:\data 和 ew。要寫成 "C:\\data\\new",或在引號前加 r 寫成 r"C:\data\new"(原始字串,反斜線不跳脫)。完整範例一:整理使用者填的資料
# 整理使用者填的資料:前後多了空白、大小寫不一
raw = " Amy Chen , AMY@Example.com "
parts = raw.split(",") # 用逗號切成兩段,得到串列(第 5 課)
name = parts[0].strip() # 第 0 段去掉前後空白 → 'Amy Chen'
email = parts[1].strip().lower() # 第 1 段去空白、轉小寫 → 'amy@example.com'
print(name, "/", email)
print("@" in email) # True:有沒有出現 @
at = email.find("@") # @ 在索引 3
user = email[:at] # 從頭切到 @ 之前(不含 @)→ 'amy'
print(at, user, user.upper()) # 3 amy AMY
print(len(name), name.replace(" ", "_")) # 8 Amy_Chen
print(raw) # 原字串完全沒有變
Amy Chen / amy@example.com
True
3 amy AMY
8 Amy_Chen
Amy Chen , AMY@Example.com
最後一行證明:前面做了 split、strip、lower、replace,原本的 raw 一個字都沒變,每一步都是做出新的字串再貼上新名字。
完整範例二:用 f-string 印收據與跳脫字元
# 用 f-string 印收據:大括號裡放變數或運算式,冒號後面寫格式
item = "拿鐵"
qty = 3
price = 45.5
total = qty * price
print(f"{item} x {qty} 杯") # 拿鐵 x 3 杯
print(f"小計:{total:.2f} 元") # .2f:固定 2 位小數 → 136.50
print(f"本月營收:{1234567:,} 元") # ,:千分位逗號 → 1,234,567
print(f"{item}\n數量:{qty}") # \n 是換行,印成兩行
print("她說:\"好喝\"") # \" 在雙引號字串裡放一個雙引號
print("C:\\data\\new") # \\ 代表一個反斜線
print("{total:.2f}") # 忘了加 f:大括號原樣印出
拿鐵 x 3 杯
小計:136.50 元
本月營收:1,234,567 元
拿鐵
數量:3
她說:"好喝"
C:\data\new
{total:.2f}
以後在哪裡會再遇到
資料分析的第一步常常是清理文字欄位:去掉前後空白、統一大小寫、把「2026/10/08」換成標準格式,pandas 的 df["name"].str.strip() 用的就是這一課的同一組方法,只是一次套用到整欄。寫 AI 應用時,送給語言模型的提示文字(prompt)也大多用 f-string 把使用者的問題、查到的資料填進範本裡。
✅ 自我檢測
6 題都是小段程式,輸出都用 Python 3.13 實際跑過。選完會立即顯示對錯與解析,全部作答後會出現總分。目前得分:0 / 6