如何把awk脚本移植到Python
推荐
在线提问>>
将一个awk脚本移植到Python主要在于代码风格而不是转译。
脚本是解决问题的有效方法,而awk是编写脚本的出色语言。它特别擅长于简单的文本处理,它可以带你完成配置文件的某些复杂重写或目录中文件名的重新格式化。
何时从awk转向Python
但是在某些方面,awk的限制开始显现出来。它没有将文件分解为模块的真正概念,它缺乏质量错误报告,并且缺少了现在被认为是编程语言工作原理的其他内容。当编程语言的这些丰富功能有助于维护关键脚本时,移植将是一个不错的选择。
我最喜欢的完美移植awk的现代编程语言是Python。
在将awk脚本移植到Python之前,通常值得考虑一下其原始使用场景。例如,由于awk的局限性,通常从Bash脚本调用awk代码,其中包括一些对sed、sort之类的其它命令行常见工具的调用。最好将所有内容转换为一个一致的Python程序。有时,脚本会做出过于宽泛的假设,例如,即使实际上只运行一个文件,该代码也可能允许任意数量的文件。
在仔细考虑了上下文并确定了要用Python替代的东西之后,该编写代码了。
标准awk到Python功能
以下Python功能是有用的,需要记住:
withopen(some_file_name)asfpin:
forlineinfpin:
pass#dosomethingwithline
此代码将逐行循环遍历文件并处理这些行。
如果要访问行号(相当于awk的NR),则可以使用以下代码:
withopen(some_file_name)asfpin:
fornr,lineinenumerate(fpin):
pass#dosomethingwithline
在Python中实现多文件的awk式行为
如果你需要能够遍历任意数量的文件同时保持行数的持续计数(类似awk的FNR),则此循环可以做到这一点:
defawk_like_lines(list_of_file_names):
def_all_lines():
forfilenameinlist_of_file_names:
withopen(filename)asfpin:
yieldfromfpin
yieldfromenumerate(_all_lines())
此语法使用Python的生成器和yieldfrom来构建迭代器,该迭代器将遍历所有行并保持一个持久计数。
如果你需要同时使用FNR和NR,这是一个更复杂的循环:
defawk_like_lines(list_of_file_names):
def_all_lines():
forfilenameinlist_of_file_names:
withopen(filename)asfpin:
yieldfromenumerate(fpin)
fornr,(fnr,line)in_all_lines:
yieldnr,fnr,line
更复杂的FNR、NR和行数的awk行为
如果FNR、NR和行数这三个你全都需要,仍然会有一些问题。如果确实如此,则使用三元组(其中两个项目是数字)会导致混淆。命名参数可使该代码更易于阅读,因此最好使用dataclass:
importdataclass
@dataclass.dataclass(frozen=True)
classAwkLikeLine:
content:str
fnr:int
nr:int
defawk_like_lines(list_of_file_names):
def_all_lines():
forfilenameinlist_of_file_names:
withopen(filename)asfpin:
yieldfromenumerate(fpin)
fornr,(fnr,line)in_all_lines:
yieldAwkLikeLine(nr=nr,fnr=fnr,line=line)
你可能想知道,为什么不一直用这种方法呢?使用其它方式的的原因是总用这种方法太复杂了。如果你的目标是把一个通用库更容易地从awk移植到Python,请考虑这样做。但是编写一个可以使你确切地了解特定情况所需的循环的方法通常更容易实现,也更容易理解(因而易于维护)。
理解awk字段
一旦有了与一行相对应的字符串,如果要转换awk程序,则通常需要将其分解为字段。Python有几种方法可以做到这一点。这将把行按任意数量的连续空格拆分,返回一个字符串列表:
line.split()
如果需要另一个字段分隔符,比如以:分隔行,则需要rstrip方法来删除最后一个换行符:
line.rstrip("\n").split(":")
完成以下操作后,列表parts将存有分解的字符串:
parts=line.rstrip("\n").split(":")
这种拆分非常适合用来处理参数,但是我们处于偏差一个的错误场景中。现在parts[0]将对应于awk的$1,parts[1]将对应于awk的$2,依此类推。之所以偏差一个,是因为awk计数“字段”从1开始,而Python从0开始计数。在awk中,$0是整个行——等同于line.rstrip("\n"),而awk的NF(字段数)更容易以len(parts)的形式得到。
移植awk字段到Python
例如,让我们将这个单行代码“如何使用awk从文件中删除重复行”转换为Python。
awk中的原始代码是:
awk'!visited[$0]++'your_file>deduplicated_file
“真实的”Python转换将是:
importcollections
importsys
visited=collections.defaultdict(int)
forlineinopen("your_file"):
did_visit=visited[line]
visited[line]+=1
ifnotdid_visit:
sys.stdout.write(line)
但是,Python比awk具有更多的数据结构。与其计数访问次数(除了知道是否看到一行,我们不使用它),为什么不记录访问的行呢?
importsys
visited=set()
forlineinopen("your_file"):
iflineinvisited:
continue
visited.add(line)
sys.stdout.write(line)
编写Python化的awk代码
Python社区提倡编写Python化的代码,这意味着它要遵循公认的代码风格。更加Python化的方法将区分唯一性和输入/输出的关注点。此更改将使对代码进行单元测试更加容易:
defunique_generator(things):
visited=set()
forthinginthings:
ifthinginvisited:
continue
visited.add(things)
yieldthing
importsys
forlineinunique_generator(open("your_file")):
sys.stdout.write(line)
将所有逻辑置于输入/输出代码之外,可以更好地分离问题,并提高代码的可用性和可测试性。
结论:Python可能是一个不错的选择
将awk脚本移植到Python时,通常是在考虑适当的Python代码风格时重新实现核心需求,而不是按条件/操作进行笨拙的音译。考虑原始上下文并产生高质量的Python解决方案。虽然有时候使用awk的Bash单行代码可以完成这项工作,但Python编码是通往更易于维护的代码的途径。
以上内容为大家介绍了如何把awk脚本移植到Python,希望对大家有所帮助,如果想要了解更多Python相关知识,请关注IT培训机构:千锋教育。http://www.mobiletrain.org/