iT邦幫忙

2

【python入門教室】(4) 超完整的python字串函數用法統整

大家好,我是心原一馬,內心原來一心喜歡打程式碼。
字串是程式語言中一種常用的資料型態,
熟悉字串則程式功力必定大增,
今天來給大家介紹字串幾個常用的函數,
以及一些巧妙的運用

一、分割、合併

函數 說明
s.split() 默認以空格、換行字元分割字串s,返回列表
s.join(seq) 以s為分隔符,將seq中的元素串起來成為一個新的字串

split例子:

S1 = "Horse-Horse-Tiger-Tiger"
print(S.split('-')) #['Horse', 'Horse', 'Tiger', 'Tiger']
print(S.split('Horse')) #['', '-', '-Tiger-Tiger']

S2 = "three     monkey   jumping  on the    bed"
print(S2.split()) #['three', 'monkey', 'jumping', 'on', 'the', 'bed']
print(S2.split(' ')) #['three', '', '', '', '', 'monkey', '', '', 'jumping', '', 'on', 'the', '', '', '', 'bed']

這邊注意S2.split()S2.split(' ')是不一樣的,
如果原始字串有多個空格,
S2.split()會將多餘的空字元去除,
S2.split(' ')會分割出許多空字元,
一般來說,以空格分割字串直接用S2.split()即可。

join例子:

seq = ["HOW", "ARE", "YOU"]
print(' '.join(seq)) # 印出 HOW ARE YOU
print('-'.join(seq)) # 印出 HOW-ARE-YOU

巧思: 簡潔語法換行印列表字串

假設我們有一個列表,裡面元素都是字串,
現在想要把列表內字串一行一行的印出來怎麼做呢?
舉例來說,
假設L=["Alice", "Bob", "Car"]
希望結果印出:
Alice
Bob
Car

最常見也最容易想的方法大概就是for迴圈了:

for s in L:
    print(s)

還有沒有其它方法呢?
這時就要用到join()了:

print('\n'.join(L))

哇,這是什麼不可思議的語法,連迴圈都省了?
首先\n是特殊字元,代表換行的意思,
因此用\n換行字元把列表裡的字串接起來,
自然就可以換行印囉。

二、查找

函數 說明
s.find(str) 返回str第一次在字串s中出現的index,若找不到則返回-1
s.count(str) 返回str在字串s中出現的次數

find例子:

s = "believe"
print(s.find("lie")) #印出2
print(s.find("le"))  #印出-1

count例子:

s = "wowow"
print(s.count('w'))  # 3
print(s.count('wow')) # 1 

三、替換

函數 說明
s.replace(str1, str2) 將s中的str1替換成str2

replace例子:

s1 = "ABBABBAAAB"
print(s1.replace('A','C')) #印出 CBBCBBCCCB

s2 = "wowwwowowoo"
print(s2.replace('wow','HO')) #印出HOwHOowoo

文末將會提一個巧妙運用哦,請期待~

四、格式調整,大小寫轉換

函數 說明
s.lower() 將字串s裡的字母全部改成小寫
s.upper() 將字串s裡的字母全部改成大寫
s.swapcase() 將字串s的字母大小寫翻轉
s.lstrip() 去除字串s左邊的空格
s.rstrip() 去除字串s右邊的空格
s.strip() 去除字串s左、右兩邊的空格
s.center(width) 返回一個居中的字串,將左右兩邊填充至長度width

範例: 再造金字塔

我們在【python入門教室】(3) 列表和字串也想做加法和乘法?做過這個範例,
在螢幕上顯示7階的金字塔,如下:

      *
     ***
    *****
   *******
  *********
 ***********
*************

你可能覺得數每一個星星前有幾個空格有點麻煩,
這邊再提供一種不一樣的思維來做。
首先,金字塔的底部有2*7-1=13顆星星,
第i行需要印的星星數= 2*i -1,
那麼我們就把每行的字串長串都填充到13,
再把它置中就好了啊,程式碼如下:

for i in range(1,8):
    print(('*' * (2*i-1)).center(13))

五、判斷組成

函數 說明
s.islower() 判斷字串s的字母是否全由小寫字母組成
s.isupper() 判斷字串s的字母是否全由大寫字母組成
s.isdigit() 判斷字串s是否全由數字組成
s.isalpha() 判斷字串s是否全由字母組成
s.isalnum() 判斷字串s是否全由字母及數字組成

呼叫上述函式的結果都是返回TrueFalse的布林值,
注意若s是空字串,上述結果會返回False

補充: 判斷字串內是否有漢字

這邊補充一下,上述函數都是判斷字串是否由英文、數字組成,
如果今天想要判斷「漢字」怎麼辦呢?
這邊示範統計一個字串由多少「漢字」、「英文」及「其它符號(含數字、空格、…)」組成
網路查詢資料說「漢字」(應該含簡體字)的範圍介於utf-8碼的[u'\u4e00', u'\u9fa5']之間
(注意isalpha()函數很特別,碰到漢字亦返回true,故需要先判斷漢字)

def countNum(string):    
    result = [0, 0, 0]   
    for char in string:        
        if u'\u4e00' <= char <= u'\u9fa5':    
            result[0] += 1        
        elif char.isalpha():
            result[1] += 1
        else:            
            result[2] += 1    
    return result 

result = countNum("汉字test @*%")
print(result)  # [2, 4, 4]

好了,關於字串常用函數就介紹到這邊了,
沒介紹到的函數大概相對也比較少用吧。

參考資料:

  1. 菜鳥教程
  2. 判斷字符串中是否存在漢字 python3

最後看一段字串函數的妙用

巧思: 去除不要的字

假設給你一個單字animal
我們希望把英文的母音(a,e,i,o,u)都去除,
想要得到結果nml
你會怎麼做呢?

這邊一步步帶你思考解法,
希望給讀者們一些啟發

先上個普通想法:
既然是去除
很多人直覺可能會想到先查一下字串有沒有類似列表的remove()函數可以用,
結果一查居然沒有…

我們來看一段程式碼進化歷程

於是有些人可能會想到說,
那我是不是可以先把字串轉換成列表,
調用列表的remove()函數,
再用字串的join()函數轉回字串呢?
例如:

>>> list('animal')
['a', 'n', 'i', 'm', 'a', 'l']

使用list()可以把字串轉換成每個字元是一個元素的列表。

一次去除所有母音好像不太好想,
我們先試做去除字母a的函數:

def removeVowel(word):
    L = list(word)
    while 'a' in L:
        L.remove('a')
    return ''.join(L)

print(removeVowel('animal')) #印出 niml

好的,恭喜你,現在已經可以去除單字裡的字母a了。

可是母音總共有五個,如果改成五個while迴圈:

def removeVowel(word):
    L = list(word)
    while 'a' in L:
        L.remove('a')
    while 'e' in L:
        L.remove('e')
    while 'i' in L:
        L.remove('i')
    while 'o' in L:
        L.remove('o')
    while 'u' in L:
        L.remove('u')
    return ''.join(L)
    
print(removeVowel('animal')) #印出 nml

可以得到正確的結果,但是也太冗長了吧?
仔細瞧,其實五個while迴圈做的事都是類似的,
可以改以for迴圈簡化它:

def removeVowel(word):
    L = list(word)
    for letter in 'aeiou':
        while letter in L:
            L.remove(letter)
    return ''.join(L)

嗯,現在看起來好多了,
然而底下以替換的思想完成去除這件事,
才堪稱是巧思啊!

「路遙知碼力」的力量

小馬發明了這樣一句諺語:

路遙知碼力,日久練成精-只要在程式之路鑽研的夠深,便能夠充分發揮程式碼的力量; 練習的日子夠久,便能夠練成寫出精簡代碼的能力。

這個想法是依序把a,e,i,o,u這五個字,把它替換成空字元:

def removeVowel(word):
    for letter in 'aeiou':
        word = word.replace(letter,'')
    return word

把字元替換成空字元,
那麼那個字也不會印出來了,
也就相當於達成去除的效果。

課後練習

今天也是小馬出題,就不在CodeWar上找了,
歡迎留言討論答案

習題: 判斷合法的變數名稱

有學過程式語言的人都知道,
幫變數取名稱有一定的限制,不能任意亂取。
一個合法的變法名稱可以由字母、數字和'_'組成,
但是開頭不可以是數字。
那麼給你一個字串,
能否讓程式來判斷這個字串是否可以當作合法的變數名稱呢?
請實作函數isLegal(s),

def isLegal(s):
    return #這邊輸入你要回傳的值

參數s表示一個非空字串
請回傳TrueFalse表示s是否可做為合法的變數名稱。

例子:

  • 合法的變數名字: _abc, aPPle, x1
  • 不合法的變數名字: 1a(數字開頭), ao@(有特殊符號), pi apple(中間有空格)

提示想法如下:
合法的命名需滿足兩個條件:

  1. 去掉'_'字元後,僅由字母和數字組成
  2. 首字不是數字

1
一級屠豬士
iT邦大師 1 級 ‧ 2020-04-09 16:23:52
>>> l1 = list('animal')
>>> l2 = list('aeiou')
>>> l3 = [x for x in l1 if x not in l2]
>>> l3
['n', 'm', 'l']
>>> l4 = list(filter(lambda x : x not in l2, l1))
>>> l4
['n', 'm', 'l']

心原一馬 iT邦研究生 5 級 ‧ 2020-04-09 16:41:03 檢舉

感謝分享,這樣解也很簡潔

1
Pondudu
iT邦新手 5 級 ‧ 2020-04-14 16:48:52

小馬哥您好^w^,我的解法如下:

def isLegal(s):
    if s.isupper() or s.islower() or s[0]=="_":
        for word in "_":
            s = s.replace(word,"")
            if  s.isalnum():
                return True
            else:
                return False
    else:
        return False

print(isLegal( "_1abe"))  #True
print(isLegal("@2fgh"))  #False
print(isLegal("Bk89 ag"))  #False

我先檢查字首是否為英文或_,如果是,再將底線刪除檢查,最後檢查是否只有數字或字母組成,本來打的是先將底線刪除,再去檢查字首,但發現這樣第二個字如果是數字就會出現False了XDDD 想請問我的想法是正確的嗎? 有更簡單的解法嗎?? 感謝!!

看更多先前的回應...收起先前的回應...
心原一馬 iT邦研究生 5 級 ‧ 2020-04-14 17:28:16 檢舉

哈囉,magicloch 邦友你好:

很高興你的留言,
你的想法非常接近正確囉,
不過在判斷aPPle時會出錯(應該是判斷成True才對),
可以想想還有哪邊漏想的

有更簡單的解法哦,不過這邊小馬先賣個關子,
待你閱讀完「【python入門教室】(8) 如果你願意一層一層一層 的剝開繁瑣邏輯 (化簡冗長的if-else教學)」此篇後,
再回過頭來思考這一題,
也許你會發現化簡答案的方法(可能是個非常漂亮的解答)

屆時,
或許你會發現自己程式功力的成長呢(っ●ω●)っ

若你有新發現,歡迎留言告訴小馬,
待你讀完【python入門教室】(8)後,
小馬會告訴你我的解法 (先小小釣你胃口,嘿嘿~)

Pondudu iT邦新手 5 級 ‧ 2020-04-14 18:20:20 檢舉

真的耶! 才發現s.isalnum()的定義是"是否為字母[和]數字組成",不是"或",所以單純字母就不行Q.Q 在中間加上

elif s.isalpha():
    return True

還是失敗XDD

好的! 但是我的吸收速度很慢,一篇要消化一兩天,只能利用瑣碎時間學習QAQ 再請小馬哥等我讀完第八篇了>"<!!
PS可以稱呼我"嘟嘟"~FB登入的沒辦法改名稱哈哈

Pondudu iT邦新手 5 級 ‧ 2020-04-14 18:23:50 檢舉

發現重新登入後就可以改暱稱了XDD(好蠢)

心原一馬 iT邦研究生 5 級 ‧ 2020-04-14 22:27:13 檢舉

嗨,嘟嘟,
很高興認識你 ^^
外號還蠻可愛的xdd

其實小馬覺得你能夠持續閱讀,
一篇消化一兩天,
算是非常積極的呢

尤其現代人忙碌的生活中,
能於百忙中抽空零碎時間學習,
相當難能可貴,
很開心你與小馬交流,給予回饋,
祝你學習順利~

心原一馬 iT邦研究生 5 級 ‧ 2020-04-14 22:33:15 檢舉

對了,程式提示:
你的第一行打s.isupper() or s.islower()會錯,
因為isupper()判斷若s都是大寫字母才是True,
islower()判斷若s都是小寫字母才是True,
所以如果s有穿插大小寫會判斷為False哦(但大小寫都用是合法的變數名稱)

Pondudu iT邦新手 5 級 ‧ 2020-04-14 23:32:18 檢舉

那是因為現在工作比較閒啦XD 希望之後忙起來還可以繼續堅持...我也很感謝小馬哥不厭其煩幫我解答(இдஇ)

天啊我把那兩個函數理解成"字首"是大寫或小寫了(||゚д゚) 犯蠢了(╥﹏╥) 重打一次:

def isLegal(s):
    if s[0].isupper() or s[0].islower() or s[0]=="_":
        for word in "_":  
            s = s.replace(word,"")  #將_替換成空字元
            if  s.isalnum():
                return True
            else:
                return False
    else:
        return False

print(isLegal("aPPle"))  #True
print(isLegal( "_1abC"))  #True
print(isLegal("998Fg"))  #False
print(isLegal("@2fgh$"))  #False
print(isLegal("Bk89 ag"))  #False

這次有對嗎>.< 之後再來向您請教更簡單的寫法!(握拳)

心原一馬 iT邦研究生 5 級 ‧ 2020-04-15 20:50:16 檢舉

嗯嗯,寫的不錯哦,這次應該是對的 ^^

Pondudu iT邦新手 5 級 ‧ 2020-04-16 09:02:35 檢舉

/images/emoticon/emoticon41.gif

1
gerryqoo
iT邦新手 5 級 ‧ 2020-06-22 14:59:11

我的解法:

def isLegal(s):
    return True if s[0].isdigit()==0 and s.replace('_','').isalnum() else False

s = input()
print(isLegal(s))
心原一馬 iT邦研究生 5 級 ‧ 2020-06-22 15:10:20 檢舉

嗨,謝謝分享,
這個解法應該是可以的,
只是可以簡單寫成

def isLegal(s):
    return s[0].isdigit()==0 and s.replace('_','').isalnum()

因為s[0].isdigit()==0 and s.replace('_','').isalnum()本來就是一個True或False的值

gerryqoo iT邦新手 5 級 ‧ 2020-06-22 15:43:15 檢舉

/images/emoticon/emoticon07.gif

我要留言

立即登入留言