如何将 awk 脚本移植到 Python


Posted in Python onDecember 09, 2019

将一个 awk 脚本移植到 Python 主要在于代码风格而不是转译。

脚本是解决问题的有效方法,而 awk 是编写脚本的出色语言。它特别擅长于简单的文本处理,它可以带你完成配置文件的某些复杂重写或目录中文件名的重新格式化。

何时从 awk 转向 Python

但是在某些方面,awk 的限制开始显现出来。它没有将文件分解为模块的真正概念,它缺乏质量错误报告,并且缺少了现在被认为是编程语言工作原理的其他内容。当编程语言的这些丰富功能有助于维护关键脚本时,移植将是一个不错的选择。

我最喜欢的完美移植 awk 的现代编程语言是 Python。

在将 awk 脚本移植到 Python 之前,通常值得考虑一下其原始使用场景。例如,由于 awk 的局限性,通常从 Bash 脚本调用 awk 代码,其中包括一些对 sed、sort 之类的其它命令行常见工具的调用。 最好将所有内容转换为一个一致的 Python 程序。有时,脚本会做出过于宽泛的假设,例如,即使实际上只运行一个文件,该代码也可能允许任意数量的文件。

在仔细考虑了上下文并确定了要用 Python 替代的东西之后,该编写代码了。

标准 awk 到 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

在 Python 中实现多文件的 awk 式行为

如果你需要能够遍历任意数量的文件同时保持行数的持续计数(类似 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 和行数的 awk 行为

如果 FNR、NR 和行数这三个你全都需要,仍然会有一些问题。如果确实如此,则使用三元组(其中两个项目是数字)会导致混淆。命名参数可使该代码更易于阅读,因此最好使用 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 字段

一旦有了与一行相对应的字符串,如果要转换 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 转换将是:

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 化的 awk 代码

Python 社区提倡编写 Python 化的代码,这意味着它要遵循公认的代码风格。更加 Python 化的方法将区分唯一性和输入/输出的关注点。此更改将使对代码进行单元测试更加容易:

def unique_generator(things):
  visited = set()
  for thing in things:
    if thing in visited:
      continue
    visited.add(things)
    yield thing
import sys
  
for line in unique_generator(open("your_file")):
  sys.stdout.write(line)

将所有逻辑置于输入/输出代码之外,可以更好地分离问题,并提高代码的可用性和可测试性。

结论:Python 可能是一个不错的选择

将 awk 脚本移植到 Python 时,通常是在考虑适当的 Python 代码风格时重新实现核心需求,而不是按条件/操作进行笨拙的音译。考虑原始上下文并产生高质量的 Python 解决方案。虽然有时候使用 awk 的 Bash 单行代码可以完成这项工作,但 Python 编码是通往更易于维护的代码的途径。

总结

以上所述是小编给大家介绍的如何将 awk 脚本移植到 Python,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
利用Python的装饰器解决Bottle框架中用户验证问题
Apr 24 Python
Python 的类、继承和多态详解
Jul 16 Python
python调用Delphi写的Dll代码示例
Dec 05 Python
Python cookbook(数据结构与算法)从任意长度的可迭代对象中分解元素操作示例
Feb 13 Python
pandas.DataFrame.to_json按行转json的方法
Jun 05 Python
Python爬虫包BeautifulSoup异常处理(二)
Jun 17 Python
pygame游戏之旅 游戏中添加显示文字
Nov 20 Python
Python OS模块实例详解
Apr 15 Python
对Django中static(静态)文件详解以及{% static %}标签的使用方法
Jul 28 Python
查看已安装tensorflow版本的方法示例
Apr 19 Python
python pyecharts 实现一个文件绘制多张图
May 13 Python
python中reload重载实例用法
Dec 15 Python
Python 读取 YUV(NV12) 视频文件实例
Dec 09 #Python
基于YUV 数据格式详解及python实现方式
Dec 09 #Python
Python编写一个验证码图片数据标注GUI程序附源码
Dec 09 #Python
Python内置方法实现字符串的秘钥加解密(推荐)
Dec 09 #Python
opencv-python 读取图像并转换颜色空间实例
Dec 09 #Python
opencv-python 提取sift特征并匹配的实例
Dec 09 #Python
python 多维高斯分布数据生成方式
Dec 09 #Python
You might like
php中怎么搜索相关联数组键值及获取之
2013/10/17 PHP
php+xml结合Ajax实现点赞功能完整实例
2015/01/30 PHP
php实现图片等比例缩放代码
2015/07/23 PHP
浅谈htmlentities 、htmlspecialchars、addslashes的使用方法
2016/12/09 PHP
prototype与jquery下Ajax实现的差别
2009/09/13 Javascript
js获取图片长和宽度的代码
2009/11/24 Javascript
麻雀虽小五脏俱全 Dojo自定义控件应用
2010/09/04 Javascript
ANT 压缩(去掉空格/注释)JS文件可提高js运行速度
2013/04/15 Javascript
js中对象的声明方式以及数组的一些用法示例
2013/12/11 Javascript
中止javascript执行的方法
2014/02/14 Javascript
基于angularjs实现图片放大镜效果
2016/08/31 Javascript
form表单转Json提交的方法(推荐)
2016/09/23 Javascript
javascript 判断是否是微信浏览器的方法
2016/10/09 Javascript
关于TypeScript中import JSON的正确姿势详解
2017/07/25 Javascript
Javascript的console['']常用输入方法汇总
2018/04/26 Javascript
Nuxt.js nuxt-link与router-link的区别说明
2020/11/06 Javascript
Python Sleep休眠函数使用简单实例
2015/02/02 Python
KMP算法精解及其Python版的代码示例
2016/06/01 Python
简单谈谈python中的Queue与多进程
2016/08/25 Python
python万年历实现代码 含运行结果
2017/05/20 Python
Python内置函数 next的具体使用方法
2017/11/24 Python
详解python之协程gevent模块
2018/06/14 Python
Python 旋转打印各种矩形的方法
2019/07/09 Python
Python实现王者荣耀自动刷金币的完整步骤
2021/01/22 Python
美国受信赖的教育产品供应商:Nest Learning
2018/06/14 全球购物
西班牙香水和化妆品购物网站:Arenal Perfumerías
2019/03/01 全球购物
什么是servlet
2012/05/08 面试题
技术人员面试提纲
2013/11/28 职场文书
生产现场工艺工程师岗位职责
2013/11/28 职场文书
小学生暑假家长评语
2014/04/17 职场文书
体育教师求职信
2014/06/30 职场文书
高中同学会活动方案
2014/08/14 职场文书
工作后的感想
2015/08/07 职场文书
远程教育学习心得体会
2016/01/23 职场文书
你会写报告?产品体验报告到底该怎么写?
2019/08/14 职场文书
Go gorilla securecookie库的安装使用详解
2022/08/14 Golang