本文选自个人电子书《Python黑魔法手册》《Python黑魔法手册》在线阅读:http://magic.iswbm.comGithub项目地址:https://github.com/iswbm/magi...从一个指定的字符串中,获取期望的数据,正常人都会想到正则表达式吧?写过正则表达式的人都知道,正则表达式入门并不难,写起来也很容易。但是正则表达式几乎没有可读性。维护起来真是让人抓狂。不要因为你写了这个正则表达式就认为你可以控制它。一个月后你可能不知道。可谓天下苦难久矣。今天给大家介绍一个好东西,可以让你免于经常性的噩梦,那就是Python中一个非常冷门的库——parse。一、真实案例以最近使用parse的一个真实案例为例。下面是ovs的流表。现在我需要收集和提取一个虚拟机(网口)有多少流量和多少数据包流经这个流表。即每个in_port对应的n_bytes和n_packets的值。cookie=0x9816da8e872d717d,duration=298506.364s,table=0,n_packets=480,n_bytes=20160,priority=10,arp,in_port="tapbbdf080b-c2"actions=resubmit(,24)如果是你,你会怎么做?先用逗号分隔,再用等号分隔提取值?大家可以试试看,写出来的代码应该和我想象的一样,没有任何意义。让我告诉你,我该怎么做?可以看到,我使用了一个叫parse的第三方包,需要自己安装$python-mpipinstallparse从上面的案例中,大家应该能感受到parse对于解析标准化字符串来说是非常强大的。2.parse的结果parse的结果只有两个结果:如果没有匹配,parse的值为None>>>parse("halo","hello")isNoneTrue>>>如果有匹配,parse的值为ResultExample>>>parse("hello","helloworld")>>>parse("hello","hello")>>>如果你写一个解析rule,字段Name没有定义字段,是一个匿名字段,Result会是一个类列表实例,演示如下:>>>profile=parse("我是{},{}岁,{}","我是杰克,27岁,男")>>>profile>>>profile[0]'Jack'>>>profile[1]'27'>>>profile[2]'male'并且如果你写了一个定义字段名称的解析规则,结果将是一个类似字典的实例,演示如下:>>>profile=parse("我是{name},{age}岁,{gender}","我是Jack,27岁,男")>>>profile>>>个人资料['姓名']'杰克'>>>个人资料['年龄']'27'>>>profile['gender']'male'3.复用pattern和使用re一样,parse也支持pattern复用。>>>fromparseimportcompile>>>>>>pattern=compile("我是{},{}岁,{}")>>>pattern.parse("我是Jack,27岁,男性")>>>>>>pattern.parse("我是Tom,26岁,male")4.类型转换从上面的例子中,你应该能注意到parse得到age的时候,变成了“27”。这是一个字符串。有没有提取的时候怎么根据我们的类型进行转换?你可以这样写。>>>fromparseimportparse>>>profile=parse("我是{name},{age:d}岁,{gender}","我是Jack,27岁,男")>>>profile>>>type(profile["age"])除了把它变成整数,还有其他格式吗?内置格式很多,比如匹配时间>>>parse('Meetat{:tg}','Meetat1/2/201111:00PM')更多类型请参考官方文档:TypeCharactersMatchedOutputlLetters(ASCII)strwLetters,numbersandunderscorestrWNotletters,numbersandunderscorestrsWhitespacestrSNon-whitespacestrdDigits(effectivelyintegernumbers)intDNNon-digitstrnNumberswithseparatorthousands()int%Percentage(convertedtovalue/100.0)floatfFixed-pointnumbersfloatFDecimalnumbersDecimaleFloating-pointnumberswithexponente.g.1.1e-10、NAN(所有大小写不敏感)floatg通用数字格式(d、f或e均可)floatbadex十进制数转八进制l数字(小写和大写)inttiISO8601格式日期/时间,例如1972-01-20T10:21:36Z(“T”和“Z”可选)datetimeteRFC2822电子邮件格式日期/时间,例如Mon,20Jan197210:21:36+1000datetimetgGlobal(day/month)formatdate/timee.g.20/1/197210:21:36AM+1:00datetimetaUS(月/日)格式日期/时间,例如1/20/197210:21:36PM+10:30datetimetcctime()格式化日期/时间,例如SunSep1601:03:521973datetimethHTTP日志格式日期/时间例如21/Nov/2011:00:07:11+0000datetimetsLinux系统日志格式日期/时间,例如Nov903:37:44datetimettTime例如晚上10:21:36-5:30时间5.解压的时候去掉空格,去掉两边的空格>>>parse('hello{},hellopython','helloworld,hellopython')>>>>>>>>>parse('hello{:^},hellopython','helloworld,hellopython')删除左边的空格>>>parse('hello{:>},你好蟒蛇','你好世界,你好蟒蛇')<结果('世界',){}>去掉右边的空格>>>parse('hello{:<},hellopython','helloworld,hellopython')6.CasesensitiveswitchParse默认是不区分大小写的,你写hello和HELLO是一样的如果需要区分大小写,可以添加参数,如下所示:>>>parse('SPAM','spam')>>>parse('SPAM','spam')是NoneFalse>>>parse('SPAM','spam',case_sensitive=True)是NoneTrue7。匹配字符数精确匹配:指定最大字符数>>>parse('{:.2}{:.2}','hello')#字符数不匹配>>>>>>parse('{:.2}{:.2}','hell')#匹配的字符数模糊匹配:指定最小字符数>>>parse('{:.2}{:2}','hello')>>>>>>parse('{:2}{:2}','hello')如果要在精确/模糊匹配模式下进行格式转换,可以这样写>>>parse('{:2}{:2}','1024')>>>>>>>>>解析('{:2d}{:2d}','1024')<结果(10,24){}>8。三个重要的属性Parsefixed中有三个非常重要的属性:named位置提取的匿名字段的元组:存储命名字段dictionaryspans:存储匹配字段的位置。下面的代码会告诉你它们之间的区别>>>profile=parse("我是{name},{age:d}岁,{}","我是Jack,27岁,男")>>>简介。固定('男性',)>>>pprofile.named{'age':27,'name':'Jack'}>>>profile.spans{0:(25,29),'age':(11,13),'name':(5,9)}>>>9.自定义类型转换匹配到的字符串会作为参数传递给对应的函数。比如我们之前说的,把字符串转成整数>>>parse("Iam{:d}","Iam27")>>>type(_[0])>>>相当于>>>defmyint(string):...returnint(string)...>>>>>>>>>parse("Iam{:myint}","Iam27",dict(myint=myint))>>>type(_[0])>>>使用它,我们可以自定义很多功能,比如我要让匹配的字符串全部大写>>>defshouty(string):...returnstring.upper()...>>>parse('{:shouty}world','helloworld',dict(shouty=shouty))>>>10总结parse库在字符串解析处理场景中提供的便利。肉眼可见,容易上手。在一些简单的场景下,使用parse可以类比使用re来写正则表达式。开发效率不知道高了几个档次,用它写的代码充满美感,可读性高,后期维护代码也没有压力,推荐大家使用。