目录
- 问题
- 解决方案
- 讨论
问题
在不同的操作系统下,怎样做字符串匹配?
解决方案
fnmatch()
模块提供两个函数,fnmatch()
以及 fnmatchcase()
可以用来执行做这样的匹配。
from fnmatch import fnmatch, fnmatchcase
match_res = fnmatch('foo.txt', '*.txt')
print(match_res)
match_res2 = fnmatch('foo.txt', '?oo.txt')
match_res3 = fnmatch('foo.txt', '?oo.txt')
match_res4 = fnmatch('Dat45.csv', 'Dat[0-9]*')
match_res5 = fnmatch('Dat45.csv', 'Dat[0-9].csv')
match_res6 = fnmatch('Dat45.csv', 'Dat[0-9][0-9].csv')
print(match_res2, match_res3, match_res4, match_res5, match_res6)
结果:
True True True False True
fnmatch()
函数也可以处理更多复杂场景的数据结构:
names = ['Dat1.csv', 'Dat2.csv', 'config.ini', 'foo.py', 'api.py']
matchs = [name for name in names if fnmatch(name, 'Dat*.csv')]
print(matchs)
结果:
['Dat1.csv', 'Dat2.csv']
一般来说,在不同的操作系统上,fnmatch()
的匹配模式所采用的大小写区分规则和底层文件系统相同,即根据操作系统的不同而有所不同。例如:
# On OS X (MAX)
fnmatch('foo.txt', '*.TXT')
>>> False
# On Windows
fnmatch('foo.txt', '*.TXT')
>>> True
但是如果大小写统一化很重要,那么我们除了使用 lower()
等方法统一化字符串大小写以外,还可以通过 fnmatchcase()
方法,完全根据我们提供的大小写方式来进行匹配。
fnmatch_res = fnmatch('foo.txt', '*.TXT')
fnmatchcase_res = fnmatchcase('foo.txt', '*.TXT')
print('fnmatch_res :', fnmatch_res)
print('fnmatchcase_res :', fnmatchcase_res)
结果:
讨论
fnmatch
模块的两个函数其实在处理非文件名式的字符串时有非常大的潜在用途:
addresses = [
'5412 N CLARK ST',
'1060 W ADDISON ST',
'1039 W GRANVILLE AVE',
'2122 N CLARK ST',
'4902 N BROADWAY'
]
address_res1 = [addr for addr in addresses if fnmatchcase(addr, '*ST')]
print(address_res1)
address_res2 = [addr for addr in addresses if fnmatchcase(addr, '54[0-9][0-9] *CLARK*')]
print(address_res2)
结果:
['5412 N CLARK ST', '1060 W ADDISON ST', '2122 N CLARK ST']
['5412 N CLARK ST']
小结:
fnmatch
所完成的匹配操作有点介乎于简单的字符串方法和全功能的正则表达式之间。但是如果只是试着在处理数据时提供一种简单的机制以允许使用通配符,那么字符串方法,fnmatch
都是合理的解决方案。
但是如果实际上想编写匹配文件名的代码,glob 模块似乎是一个更好的选择。