关山难越,谁悲失路之人;萍水相逢,尽是他乡之客。
百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程教程 > 技术文章 > 正文

批量处理文件,除了 Python,不妨试试 VIM

guanshanw 2023-08-22 02:52 65 浏览 0 评论

来源:早起Python

作者:大奎

批量处理文件,除了 Python,不妨试试 VIM

大家好,我是早起。

在之前的办公自动化系列文章,我们大多基于 Python 实现,因为使用 Python 具有灵活、强大的特点。使用 VIM 具有快速、可视化的优势。两者对大量同构文本进行修改,可大幅提高工作效率。但相较于编写 Python 程序,VIM 可视化执行更胜一筹。

这也提示我们,Python 不是万能的——至少在某些方面、某些场景下,不一定是最优解。合适的工具运用到合适的场合是效率最高的方式。不能自己是锤子,看什么就都是钉子。

在对 VIM 不熟悉的用户看来,VIM 的操作过程可能更复杂、难懂。但这是先入为主的印象,VIM 处理文本还是很方便快捷的:我们有了 Python 这把锤子,不排斥再来 VIM 这个锯嘛,这样才能“工欲善其事,必先利其器”。本文将对比使用 Python 和 VIM 对同一个文本编辑任务处理的情况。

01、需求说明

有大量类似结构的文本文件需要处理,目录结构如下:

E:.
└─content
    ├─a
    │ └──content.txt
    ├─b
    │ └──content.txt
    └─c
      └──content.txt 

其中的每个文件 content .txt内容结构如下:

<vsbimg src="/_vsl/012A716D176AFA6EBBAF64BD4CB63BCA/994A4168/AE5BB"></vsbimg>
<vsbimg src="/_vsl/2ADBFFCE33AAE9B2E79E758EF6AD5626/CEFD12BB/A8DC5"></vsbimg>

要求是:

  • <vsbimg></vsbimg> 标签改为 <img></img> 标签。
  • /_vsl/012A7 表示的相对地址,变成另一个 URL 地址,如 http://image.xx.com/image/
  • 将 src 的最后两个/改为 _。
  • 将整个 src 最后加上图片后缀 .png。

修改后的文件为:

<img src="http://image.xx.com/image/16D176AFA6EBBAF64BD4CB63BCA_994A4168_AE5BB.png"></img>
<img src="http://image.xx.com/image/FCE33AAE9B2E79E758EF6AD5626_CEFD12BB_A8DC5.png"></img>

02、Python实现

首先让我们用 Python 编写程序来完成,代码比较简单,但面对如此简单的问题,写一个程序还是“高射炮打蚊子” 了。而且调试 Python 正则表达式,并不是一个直观的过程。

import os
import re


def rep(strs):
    strs = re.sub(r'<vsbimg',r'<img',strs)
    strs = re.sub(r'<\/vsbimg',r'</img',strs)
    strs = re.sub(r'(/_vsl/.*?)/',r'\1_',strs)
    strs = re.sub(r'(/_vsl/.*?)/',r'\1_',strs)
    strs = re.sub(r'(src=".*?)"',r'\1.png"',strs)
    strs = re.sub(r'src="/_vsl/.{5}',r'src="http://image.x.com/image/',strs)
    return strs


def op(fn):
    fn2 = os.path.join(os.path.split(fn)[0],os.path.split(fn)[1]+'new')
    with open(fn,encoding='utf-8') as f,open(fn2,'w',encoding='utf-8') as f2:
        for l in f.readlines():
            l = rep(l)
            f2.write(l)

for r,_,fs in os.walk('content'):
    for f in fs:
        if f.endswith('txt'):
            fn = os.path.join(r,f)
            op(fn)

杀鸡不用牛刀,咱们改用 VIM 试试。

VIM 最主要好处就是:构造查找正则表达式时结果可视化,这样就可以逐步求精地写正则表达式,反之刚才写程序时,我得来回测试,十分费力。

03、VIM实现

下面是使用 VIM 实现需求所需要注意的几点

  • 本例使用 VIM 中的 :%s 替换指令很容易完成替换操作。正则表达式构造需要慢慢来。
  • 如果牵涉到复杂替换时,还需要对搜索结果分组,以便使用分组结果。
  • 为了批量完成序列替换操作,需要将操作写入批处理脚本,再用 :source 执行脚本。
  • 以上操作在单文件中执行,为了在许多文件中同时完成,需要使用缓冲区执行 :bufdo 命令。

3.1 构造正则表达式搜索

为了替换 <vsbimg,我们构造一个查找正则表达式。

构造出的表达式如下:

/<vsbimg

这个表达式搜索了 <vsbimg 开头的所有内容。

/ 指令后按向上箭头表示上一次输入的查询历史。按 q/ 表示所有查询历史,可以在此历史上修改,这样就可以逐步精化。

3.2 替换

常规替换指令 :%s/pattern/string/g,留空的查询域表示上次搜索的结果。在上步查询基础上,我们可以使用 :%s//<img/g 的方式完成更改。

这个操作很重要:很多复杂的正则表达式,不可能一步直接构造出来;采用搜索的方法,可以高亮显示每次的搜索结果,进而改进正则表达式。而替换时留空查找域,直接表示上次搜索结果,极大方便了替换操作。使一步替换操作转换为:搜索,替换两步,降低了难度,提高了效率。

注意以下替换语句,使用了 \ 转义字符来匹配 </vsbimg> 的特殊字符 \

:%s/<\/vsbimg/<\/img/g

3.3 搜索结果分组、使用

在对 \ 转换为 _ 的操作中,我们需要记住之前的匹配对象,用来在替换时作为不改变的内容引用。

这里用 () 圈起来需要分组的部分,在搜索或者替换部分用 \1 表示第一个分组,以此类推。具体看代码:

:%s/\("\/_vsl\/.\{-1,}\)\//\1_/g 

因为有两个 \,所以需要执行两次。

替换域里的 \1 指代的是 () 中的匹配内容,也就是 src\_vsb/ 之后遇到的第一个 \ 为止的内容。当替换时,我们依然把这部分,用 \1 使用上,只是把 \ 改为\_

3.4 非贪婪模式

上例子可见 .\{-1,} 的代码,这是对任意字符进行非贪婪匹配,以缩小 / 适配范围,适配到第一个 / 为止,不再继续贪婪最大适配。

在给 src 添加 .png 后缀时,也使用了分组和非贪婪概念。将 src 到第一个"的内容视为一个分组,然后替换为分组内容和 .png"

:%s/\(src=".\{-1,}\)"/\1.png"/g

将相对地址修改为 URL 时,URL 部分需要进行很多次转义。

:%s/src="\/_vsl\/.\{5\}/src="http:\/\/192\.168\.22\.117\/cnv\/jflyfox\/mtg\/cnvImage\//g

最后,我们把以上修改保存进原文件:w

以上,我们通过搜索和替换操作,完成了对单个文件的修改。

如果对每一个文件都执行如上的程序,就显得比较复杂了,好在 VIM 支持批处理操作。

3.5 批处理文件执行 source

这里,我们将以上操作步骤,写到 oper.vim 文件中去。

:%s/<vsbimg/<img/ge
:%s/<\/vsbimg/<\/img/ge
:%s/\("\/_vsl\/.\{-1,}\)\//\1_/ge
:%s/\("\/_vsl\/.\{-1,}\)\//\1_/ge
:%s/\(src=".\{-1,}\)"/\1.png"/ge
:%s/src="\/_vsl\/.\{5\}/src="http:\/\/192\.168\.22\.117\/cnv\/jflyfox\/mtg\/cnvImage\//ge
:w

在另一个新的待处理文件中,我们输入 :source oper.vim,就将以上所有操作在新文件中重做。

操作一个新文件可行了,如何操作大批量的文件呢?

q: 表示所有替换历史,将这些替换命令拷贝出来,避免输入带来的麻烦和错误。

3.6 缓冲区批量执行 bufdo

VIM 的 Buffer 缓冲区,相当于内存。当我们具体修改某个文件时,实际是在内存中对他进行修改,只有当输入 :w 命令时,修改才写回硬盘。

使用 vim a.txt b.txt 指令,一次性打开两个文件,当前访问和修改的是 a.txt。使用指令 :bnext 在缓冲区之间跳转。指令 :ls 列出了当前所有缓冲区文件。

使用 vim *.txt,批量打开 txt 后缀的文件。

在当前缓冲区列表上的所有文件执行命令,输入 :bufdo excommand

本文中我们打开目录 a,b,c 下的 content.txt 文件,使用 vim content/*/*.txt 即可。在打开的窗口中执行 :ls 即可查看当前缓冲区文件。确认无误后,执行 :bufdo source oper.vim ,即可完成对所有缓冲区文件的修改。

抑制错误:当我们使用以上 vim 脚本时,很容易因为搜索规则或者文本问题导致出错,进而导致脚本停止。在每个替换语句之后加上 e ,用来表示抑制错误,就可以修正这个问题。

04、小结

使用 VIM 中的替换指令很容易完成操作。但正则表达式构造需要慢慢来。逐步求精,还可能需要分组和非贪婪模式。批处理文件 .vim 和 :source 命令可以大大简化工作。缓冲区列表执行 :bufdo 命令则进一步提高工作效率。

VIM 编辑器处理这个问题,使用的技巧都比较通用,可以迁移到其他文本处理任务中。最主要的是,构造正则表达式的过程是直接反馈、可视化的,利于构造复杂表达式。

Python 不是万能的——至少在某些方面、某些场景下,不一定是最优解。合适的工具运用到合适的场合是效率最高的方式。不能自已是锤子,看什么就都是钉子。

相关推荐

七条简单命令让您玩转Git
七条简单命令让您玩转Git

凭借着出色的协作能力、快速部署效果与代码构建辅助作用,Git已经得到越来越多企业用户的青睐。除了用于开发商业及消费级应用之外,众多科学及政府机构也开始尝试使用这...

2023-10-07 12:14 guanshanw

基本完整的关于Git分支branch的操作
基本完整的关于Git分支branch的操作

Git使用背景项目中要用到dev或者其他分支开发完代码,需要将该分支合并到master的需求操作步骤下面以dev名称为lex为分支名为例来操作一遍客户端操作:...

2023-10-07 12:14 guanshanw

Git 进阶(合并与变基)
Git 进阶(合并与变基)

在Git中整合来自不同分支的修改主要有两种方法:合并(merge)以及变基(rebase)合并(merge)merge流程图merge的原理是找到这两个分...

2023-10-07 12:13 guanshanw

Git学习笔记 003 Git进阶功能 part5 合并(第一部分)

合并(merge)是很常用的操作。尤其是一个庞大的很多人参与开发的企业级应用。一般会设定一个主分支,和多个副分支。在副分支开发完成后,合并到主分支中。始终保持主分支是一个完整的,稳定的最新状态的分支。...

非标题党,三张图帮你理解git merge和git rebase的区别
非标题党,三张图帮你理解git merge和git rebase的区别

初始场景:基于正常的开发分支修改几个小bug,然后在合并到开发分支上。gitmergegitcheckoutfeaturegitmergeho...

2023-10-07 12:13 guanshanw

git 初次使用(01)
git 初次使用(01)

先从github上克隆代码下来:使用vscode克隆代码如下图,填写上github仓库地址:vscode有时候克隆代码速度比较慢,可以用命令行方式克隆gitc...

2023-10-07 12:12 guanshanw

Git 远程操作

4.Git远程操作命令说明gitremote远程版本库操作gitfetch从远程获取版本库gitpull下载远程代码并合并gitpush上传远程代码并合并4.1远程版本库操作gitre...

Git常用命令-总结
Git常用命令-总结

创建git用户$gitconfig--globaluser.name"YourName"$gitconfig--globaluser.em...

2023-10-07 12:12 guanshanw

git中删除从别人clone下来项目的git信息,并修改为自己的分支

如果你从别人的Git存储库中克隆了一个项目,并想要删除与该存储库相关的Git信息,并将其修改为你自己的分支,则可以执行以下步骤:使用gitclone命令克隆存储库:gitclone<u...

git系列-回滚和放弃本地修改

回滚历史提交就是reset的功能。这种情况是已经提交远程仓库,需要回滚到之前的提交。gitreset--hardcommitId//注:强制提交后,当前版本后面的提交版本将会删掉!gi...

GIT使用小技巧大全
GIT使用小技巧大全

在大型软件工程的开发过程中,版本控制是无法绕过去的;目前来说,最火的版本控制软件就是GIT了。早两年SVN比较火,不过被大神linus喷了几次后,就日落西山了,...

2023-10-07 12:11 guanshanw

git相关命令-上
git相关命令-上

这些命令都是看了文档后,个人觉得比较有用的一些,展示给大家。回到远程仓库的状态抛弃本地所有的修改,回到远程仓库的状态。gitfetch--all&...

2023-10-07 12:10 guanshanw

Git命令行接口:掌握Git的必备技能
Git命令行接口:掌握Git的必备技能

Git是一款强大的分布式版本控制工具,它支持命令行界面操作。熟练掌握Git命令行接口,是开发者使用Git的必备技能之一。在这篇文章中,我们将介绍Git命令行接口...

2023-10-07 12:10 guanshanw

Git命令详解
Git命令详解

相信各位小伙伴们应该都对git有一些了解,毕竟作为代码管理的神器,就算不是IT行业的小伙伴肯定也或多或少的听说过一些。今天就来和小伙伴们分享一下自己总结的常用命...

2023-10-07 12:10 guanshanw

工作7年收集到的git命令
工作7年收集到的git命令

概念git中的术语解释:仓库也叫版本库(repository)stage:暂存区,add后会存到暂存区,commit后提交到版本库git安装linux...

2023-10-07 12:10 guanshanw

取消回复欢迎 发表评论: