日常寫代碼時候會遇到一些字符串替換的操作,比如把一大堆"駝峰"形式的字符串批量轉換成下划線形式。"駝峰"形式的變量命名風格在Java中很常見,而下划線形式的變量命名風格在C、Python等語言的代碼中更常見一些,兩者沒有嚴格的好壞區分。本文就用"駝峰"和"下划線"相互轉換的實例,講解一下Python的re模塊sub函數的強大功能。
什么是"駝峰"和"下划線"風格的字符串
變量名、函數名等標識符的多個單詞之間用下划線隔開,這樣的字符串就是下划線風格的字符串,比如:
person_info
ipv6_address
book_id
get_tomorrow_weather()
而駝峰風格的字符串就是不同單詞之間用大寫字母進行分隔,比如:
personInfo
ipv6Address
bookId
getTomorrowWeather()
re.sub函數
re.sub函數是Python內置的re模塊的一個字符串替換函數,支持正則替換。函數文檔如下:
help(re.sub)
Help on function sub in module re:
sub(pattern, repl, string, count=0, flags=0)
Return the string obtained by replacing the leftmost
non-overlapping occurrences of the pattern in string by the
replacement repl. repl can be either a string or a callable;
if a string, backslash escapes in it are processed. If it is
a callable, it's passed the match object and must return
a replacement string to be used.
re.sub函數的函數原型為:sub(pattern, repl, string, count=0, flags=0)
下面簡單介紹一下每個參數的含義:
-
pattern:是一個正則表達式,匹配要替換的子串。
-
repl:可以是一個字符串,支持對pattern中分組的后向引用。注意到文檔的最后一句話:
If it is a callable, it's passed the match object and must return a replacement string to be used.
可見,repl也可以是一個callable對象(函數),這個函數的入參為pattern正則匹配到的對象,返回值為一個字符串,表示要替換成的字符串。
注:正則的分組及后向引用詳見:python正則表達式系列(4)——分組和后向引用
-
string:要進行替換操作的字符串。
-
count:要替換掉多少個子串(按從左到右的順序),默認值為0,表示替換能被pattern匹配到的所有子串。
-
flags:正則內置屬性,默認值為0,表示不使用任何內置屬性。
注:正則內置屬性的用法詳見:python正則表達式系列(3)——正則內置屬性
"駝峰"和"下划線"字符串之間的相互轉換
通過對re.sub函數的深入了解,現在應該可以輕松寫出"駝峰"和"下划線"字符串相互轉換的代碼了。直接上代碼:
# coding:utf-8
import re
def hump2underline(hunp_str):
'''
駝峰形式字符串轉成下划線形式
:param hunp_str: 駝峰形式字符串
:return: 字母全小寫的下划線形式字符串
'''
# 匹配正則,匹配小寫字母和大寫字母的分界位置
p = re.compile(r'([a-z]|\d)([A-Z])')
# 這里第二個參數使用了正則分組的后向引用
sub = re.sub(p, r'\1_\2', hunp_str).lower()
return sub
def underline2hump(underline_str):
'''
下划線形式字符串轉成駝峰形式
:param underline_str: 下划線形式字符串
:return: 駝峰形式字符串
'''
# 這里re.sub()函數第二個替換參數用到了一個匿名回調函數,回調函數的參數x為一個匹配對象,返回值為一個處理后的字符串
sub = re.sub(r'(_\w)',lambda x:x.group(1)[1].upper(),underline_str)
return sub
代碼中已經有詳細的注釋,還是比較好理解的。下面對這兩個函數進行測試:
def test_hump2underline():
# 供測試用的一些駝峰形式的字符串
attr1 = 'PersonNamePattern'
attr2 = 'IPv6Address'
attr3 = 'personDetailInfo'
attr4 = 'CCTV'
attr5 = 'CCTVAddress'
attr6 = 'name'
attrs = [attr1,attr2,attr3,attr4,attr5,attr6]
# 遍歷attrs進行匹配和轉換,把駝峰形式的字符串轉成下划線形式
for attr in attrs:
sub = hump2underline(attr)
print sub
# 輸出:
'''
person_name_pattern
ipv6_address
person_detail_info
cctv
cctvaddress
name
'''
def test_underline2hump():
attr1 = 'person_name_pattern'
attr2 = 'ipv6_address'
attr3 = 'person_detail_info'
attr4 = 'cctv'
attr5 = 'cctvaddress'
attr6 = 'name'
attrs = [attr1, attr2, attr3, attr4, attr5, attr6]
for attr in attrs:
sub = underline2hump(attr)
print sub
# 輸出:
'''
personNamePattern
ipv6Address
personDetailInfo
cctv
cctvaddress
name
'''
JSON字符串字段名的"駝峰"轉"下划線"
JSON是一種非常通用、輕量型的數據交換格式,與Python中的字典、Java中的Map具有相同的結構。JSON中的字段名一般需要寫成下划線的形式,但是有時候也會遇到字段名是"駝峰"形式的JSON文本,那么如何把一個JSON字符串中的所有字段名都從駝峰形式替換成下划線形式呢?
因為考慮到json可能具有多層嵌套的復雜結構,所以下面直接采用正則文本替換的方式進行處理,而不是采用把JSON字符串轉成字典再進行處理。
上代碼:
def json_hump2underline(hump_json_str):
'''
把一個json字符串中的所有字段名都從駝峰形式替換成下划線形式。
注意點:因為考慮到json可能具有多層嵌套的復雜結構,所以這里直接采用正則文本替換的方式進行處理,而不是采用把json轉成字典再進行處理的方式
:param hump_json_str: 字段名為駝峰形式的json字符串
:return: 字段名為下划線形式的json字符串
'''
# 從json字符串中匹配字段名的正則
# 注:這里的字段名只考慮由英文字母、數字、下划線組成
attr_ptn = re.compile(r'"\s*(\w+)\s*"\s*:')
# 使用hump2underline函數作為re.sub函數第二個參數的回調函數
sub = re.sub(attr_ptn,lambda x : '"' + hump2underline(x.group(1)) + '" :',hump_json_str)
return sub
對上面這個函數進行測試:
def test_json_hump2underline():
# 待測試json字符串
json_str = '''
{
"englishName":"Tom",
"age":18,
"detailInfoTable": {
"address":"USA",
"sportsHobby": ["Basketball","Football","Swimming"],
"contactList":{
"tel" : "1234567",
"emailAddress":"tom@test.com"
}
},
"gender":"male"
}
'''
print json_hump2underline(json_str)
# 輸出:
'''
{
"english_name" :"Tom",
"age" :18,
"detail_info_table" : {
"address" :"USA",
"sports_hobby" : ["Basketball","Football","Swimming"],
"contact_list" :{
"tel" : "1234567",
"email_address" :"tom@test.com"
}
},
"gender" :"male"
}
'''
總結
經過以上實例可以看出,re.sub函數因為支持了正則替換及回調函數替換,在處理復雜文本替換需求時具有強大的優勢,再一次展現了Python在文本處理領域功能強大又簡單、易用的特點。
