本文共 6050 字,大约阅读时间需要 20 分钟。
python awk
脚本是重复解决问题的有效方法,而awk是编写脚本的出色语言。 它特别擅长于简单的文本处理,并且可以带您完成配置文件的某些复杂重写或目录中文件名的重新格式化。
但是到了某个时候,awk的局限性开始显现出来。 它没有将文件分解为模块的实际概念,它没有质量错误报告,并且缺少了现在被认为是语言工作原理的其他内容。 当编程语言的这些丰富功能有助于维护关键脚本时,移植将是一个不错的选择。
我最喜欢的完美移植awk的现代编程语言是Python。
在仔细考虑了上下文并确定了要用Python替代的东西之后,该编写代码了。
要记住以下Python功能:
with open ( some_file_name ) as fpin: for line in fpin: pass # do something with line
此代码将逐行循环遍历文件并处理这些行。
如果要访问行号(相当于awk的NR ),则可以使用以下代码:
with open ( some_file_name ) as fpin: for nr , line in enumerate ( fpin ) : pass # do something with line
如果您需要能够遍历任意数量的文件同时保持行数的持久计数(例如awk的FNR ),则此循环可以做到这一点:
def awk_like_lines ( list_of_file_names ) : def _all_lines ( ) : for filename in list_of_file_names: with open ( filename ) as fpin: yield from fpin yield from enumerate ( _all_lines ( ) )
该语法使用Python的生成器和yield from构建遍历所有行并保持持久计数的迭代器 。
如果您需要FNR和NR等效,这是一个更复杂的循环:
def awk_like_lines ( list_of_file_names ) : def _all_lines ( ) : for filename in list_of_file_names: with open ( filename ) as fpin: yield from enumerate ( fpin ) for nr , ( fnr , line ) in _all_lines: yield nr , fnr , line
问题是否仍然存在,是否需要全部三个: FNR , NR和line 。 如果确实如此,则使用三元组(其中两个项目是数字)会引起混乱。 命名参数可使该代码更易于阅读,因此最好使用dataclass :
import dataclass @ dataclass. dataclass ( frozen = True ) class AwkLikeLine: content: str fnr: int nr: int def awk_like_lines ( list_of_file_names ) : def _all_lines ( ) : for filename in list_of_file_names: with open ( filename ) as fpin: yield from enumerate ( fpin ) for nr , ( fnr , line ) in _all_lines: yield AwkLikeLine ( nr = nr , fnr = fnr , line = line )
您可能想知道,为什么不从这种方法开始呢? 从其他地方开始的原因是,这几乎总是太复杂了。 如果您的目标是使通用库更容易将awk移植到Python,请考虑这样做。 但是编写一个可以使您确切地了解特定情况所需的循环的方法通常更容易实现,也更容易理解(因而易于维护)。
一旦拥有与一行相对应的字符串,如果要转换awk程序,则通常需要将其分解为field 。 Python有几种方法可以做到这一点。 这将返回一个字符串列表,将行分割为任意数量的连续空格:
line. split ( )
如果需要另一个字段分隔符,则类似这样的行将用:分隔; 需要rstrip方法来删除最后一个换行符:
line. rstrip ( " \n " ) . split ( ":" )
完成以下操作后,列表部分将具有分解字符串:
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)检索。
例如,让我们将单行代码从“ ”转换为Python。
awk中的原始文件是:
awk '!visited[$0]++' your_file > deduplicated_file
“真实的” Python转换将是:
import collections import sys visited = collections . defaultdict ( int ) for line in open ( "your_file" ) : did_visit = visited [ line ] visited [ line ] + = 1 if not did_visit: sys . stdout . write ( line )
但是,Python比awk具有更多的数据结构。 相反计数的访问(我们不使用,除非知道我们是否看到了一个线),为什么参观线路没有记录?
import sys visited = set ( ) for line in open ( "your_file" ) : if line in visited: continue visited. add ( line ) sys . stdout . write ( line )
Python社区提倡编写Pythonic代码,这意味着它遵循公认的代码风格。 更加Python化的方法将区分唯一性和输入/输出的关注点。 此更改将使对代码进行单元测试更加容易:
def unique_generator ( things ) : visited = set ( ) for thing in things: if thing in visited: continue visited. add ( thing ) yield thing import sys for line in unique_generator ( open ( "your_file" ) ) : sys . stdout . write ( line )
将所有逻辑置于输入/输出代码之外,可以更好地分离问题,并提高代码的可用性和可测试性。
将awk脚本移植到Python时,通常是在考虑适当的Python语言代码样式时重新实现核心需求,而不是通过条件/操作对条件/操作进行笨拙的音译。 考虑原始上下文并产生高质量的Python解决方案。 虽然有时候使用awk的Bash单行代码可以完成这项工作,但Python编码是通往更易于维护的代码的途径。
另外,如果您正在编写awk脚本,我相信您也可以学习Python! 如果您有任何疑问,请告诉我。
翻译自:
python awk
转载地址:http://ppszd.baihongyu.com/