[雕虫小技 03] 在 Excel 中右起寻位以及如何利用这一点帮助重命名文件
假如在 Excel 中,A1 单元格内有字段「Das.Leben.der.Anderen.bluray.1080p.dts.mkv」,那么如何将其在 B1 单元格内处理成「Das Leben der Anderen bluray 1080p dts.mkv」——也即,除了后缀名前的点不替换外,其他的文件命中的点全部替换为空格?
为什么会有这么奇怪的需求?——因为我们有许多许多文件需要重命名。而且它们都是这样,我们需要将文件名中的点取代为空格,但副档名前的空格不能变。比如除了上面那个文件外,我们接下来要讲「Mona.Baker.-.In.Other.Words.A.Coursebook.on.Translation.-.Routledge.(2018).epub」重命名为「Mona Baker - In Other Words A Coursebook on Translation - Routledge (2018).epub」,以及还有好多好多这样类似的文件。
那么要这样做,最快的办法肯定是撰写一个批处理文件 (.bat).
首先我们要将这些需要重命名的文件放在一个文件夹 (folder) 内,然后在空白处按一下右键,打开一个命令提示窗口。取决于您的系统版本以及使用的是何种终端,上来先输入一个chcp 65001总没错(因为若报错则说明不需要),然后使用dir /b > list.txt将这些待重命名的文件的文件名输出至 list.txt 文件中。打开这个文本文件,将其中的「list.txt」一行删除(因为列出文件名的时候它将本身也列了进去),然后将剩余部分复制到一个新建的 Excel 表格中的 A 列。也即类似——
<table><tr><th> </th><th>A</th></tr><tr><td>1</td><td>Das.Leben.der.Anderen.bluray.1080p.dts.mkv</td></tr><tr><td>2</td><td>Mona.Baker.-.In.Other.Words.A.Coursebook.on.Translation.-.Routledge.(2018).epub</td></tr><tr><td>3</td><td>…</td></tr></table>
这里的第一个问题就是如何定义后缀名前面的点的位置。通过观察可以发现,后缀名前面的点是右起第一个点。那么问题来了,Find() 函数是左起寻位的,那么如何定位右边第一这样的概念呢?
答案是——可以结合 Substitute() 函数来达到右起寻位的效果。
首先,我们用 Substitute() 将最右侧的点替换为「|」——由于这个字符不可能出现在文件名当中,所以可以保证不会有什么重复。那么就是:
<table style="margin-left: 1.5em; width: 100%; border: none !important;"> <tr><td style="border: none !important; width: 90%; vertical-align: top !important;">=Substitute(A1,".","|", ______)</td><td style="border: none !important; text-align: right; vertical-align: top !important;">(1)</td></tr> </table>
上边下划线部分的意思是,就如何表示「最右边的点」这一点,先留空。就假设这个下划线上写出了一个式子,可以表示这是最右边点的位置。
那么,由于只替换最右侧的点为竖线,所以要数出这个字段里面总共有多少个点。此处可连用 Len() 和 Substitute(), 为:
<table style="margin-left: 1.5em; width: 100%; border: none !important;"> <tr><td style="border: none !important; width: 90%; vertical-align: top !important;">Len(A1)-Len(Substitute(A1,".",""))</td><td style="border: none !important; text-align: right; vertical-align: top !important;">(2)</td></tr> </table>
即用 A1 的总长减去将所有的点替换为空(意思是「什么都没有」,而不是「空格」)后的总长,这样得到总共的点数。把这段表达带入将最右侧的点替换为竖线的式一中的下划线处,即为:
<table style="margin-left: 1.5em; width: 100%; border: none !important;"> <tr><td style="border: none !important; width: 90%; vertical-align: top !important;">=Substitute(A1,".","|",Len(A1)-Len(Substitute(A1,".","")))</td><td style="border: none !important; text-align: right; vertical-align: top !important;">(3)</td></tr> </table>
这样得到的字段即为仅将最右侧后缀名前面的点替换为竖线的一个字段。然后用 Find() 找到这个竖线所在的位址,就可以确定后缀名前面的点在原字段中的位数了,带入得:
<table style="margin-left: 1.5em; width: 100%; border: none !important;"> <tr><td style="border: none !important; width: 90%; vertical-align: top !important;">=Find("|",Substitute(A1,".","|",Len(A1)-Len(Substitute(A1,".",""))))</td><td style="border: none !important; text-align: right; vertical-align: top !important;">(4)</td></tr> </table>
这样其实本文标题中的目的已经达到了——联合使用 Substitute() 和 Find() 进行右起寻位(从右侧起寻到某个特定字符第一次出现的位数)。
下面使用这个方法将本文开头所需要做成的例子做完。之后的部份就十分轻松了,在寻出来这个最右点的位数后,在其位数减一的左侧部份用 Substitute() 将点替换为空格,并合并该点所在位起的剩余原字段内容。整个式子写出来就是:
<table style="margin-left: 1.5em; width: 100%; border: none !important;"> <tr><td style="border: none !important; width: 90%; vertical-align: top !important;">=Substitute(Left(A1,Find("|",Substitute(A1,".","|",Len(A1)-Len(Substitute(A1,".",""))))-1),"."," ")&Right(A1,Len(A1)-Find("|",Substitute(A1,".","|",Len(A1)-Len(Substitute(A1,".",""))))+1)</td><td style="border: none !important; text-align: right; vertical-align: top !important;">(5)</td></tr> </table>
虽然看起来好像很绕,但其实一步步分析的话,完全是个很简单的过程啦。
不过……如果需要改名的都是类似「locationbar.popup.classic.with.two.lined.results.uc.js」这样的文件,而你想将它换为「locationbar_popup_classic_with_two_lined_results.uc.js」的样子,那该怎么做呢?其实很简单,只需在所有用到式二的部分的末尾都加上一个「-1」即可,也即将式二写成:
<table style="margin-left: 1.5em; width: 100%; border: none !important;"> <tr><td style="border: none !important; width: 90%; vertical-align: top !important;">Len(A1)-Len(Substitute(A1,".",""))-1</td><td style="border: none !important; text-align: right; vertical-align: top !important;">(6)</td></tr> </table>
由于不再是将「.」换为空格,因此最终的式子改成如下所示即可:
<table style="margin-left: 1.5em; width: 100%; border: none !important;"> <tr><td style="border: none !important; width: 90%; vertical-align: top !important;">=Substitute(Left(A1,Find("|",Substitute(A1,".","|",Len(A1)-Len(Substitute(A1,".",""))-1))-1),".","_")&Right(A1,Len(A1)-Find("|",Substitute(A1,".","|",Len(A1)-Len(Substitute(A1,".",""))-1))+1)</td><td style="border: none !important; text-align: right; vertical-align: top !important;">(7)</td></tr> </table>
如果是要保留自倒数第三个点起呢?那就将式二后面加上「-2」,依此类推。所幸常见的文件的扩展名都是一个点的,常见的带有两个点的也就是 .uc.js 或 .tar.gz 之类了吧。不过我没深入瞭解过,只是感觉平时见得真不多。而如果文件名中混杂有双点后缀名和单点后缀名的,由于双点后缀名常见的就那几个,因此可以很轻松地用 If() 将这几个预先挑出来执行式七,然后在 FALSE 条件下执行式五即可。
如上所述,这样做了之后,只消双击 B1 单元格的右下角,即可将其自动填充,待 A, B 两列都准备好后,只消在 C1 中输入:
<table style="margin-left: 1.5em; width: 100%; border: none !important;"> <tr><td style="border: none !important; width: 90%; vertical-align: top !important;">="ren "&Char(34)&A1&Char(34)&" "&Char(34)&B1&Char(34)</td><td style="border: none !important; text-align: right; vertical-align: top !important;">(8)</td></tr> </table>
然后向下自动填充,将 C 列全部复制到载有需要重命名的文件的目录下的一个新建的 .txt 文档里,在首行再写入chcp 65001并敲回车,保存这个 .txt 文件——如果您不是使用的诸如 Notepad++ 等纯文本编辑器且已将预设编码设为 UTF-8 的话,请切记要将 Encoding 处改为 UTF-8——然后将该 .txt 重命名为 .bat, 双击执行即可完成批量重命名了。
▋ 题外话
或曰:你这个太麻烦了,这样的需求就应该写 .py 脚本去做/写个正则表达就好了嘛/为什么不用 Perl?
对曰:这里主要是介绍用 Excel 达成右起寻位功能的小技巧,顺带,用 Excel 整理一下文件名。在本例中,我们只进行了这样一步简单的操作,但是在实践中,面对大量的需要各种重命名的一堆文件(重命名的规则不见得一致甚至可能自己都没有决定好),将文件名导出到 Excel 表格中然后反复透过各种公式去测试它们最好的表现形式——比如 B 列得到后,C 列继续做一些更改,D 列继续修,最终 E 列是成品,然后变成将 A 列文件名重命名为 E 列文件名,整个过程不是预先想好的,而是便写边看(左列结果)边改的。将文件名中的点替换为空格或者下划线,只是处理文件名中的一步而已。
如果就是为了某种定性的毫无悬念的重命名,比如双击一下就可以将本文件夹内的图片全部重命名为%Y%m%dT%H%M%S的样式,那确实 .py 脚本更方便。
<table style="border-bottom: none !important;"> <tr> <td style="border-left: none !important; border-top: none !important; border-bottom: none !important; text-align: right; border-right: 1px solid #dddddd; padding-right: 0.36rem;"> 111 年 1 月 27 日 ㈭ 晚
夏曆辛丑年臘月廿五
27 Jan 70 Eliz 2 (Thu) 18:50 </td> <td style="border: none !important; text-align: left; padding-left: 0.3rem;"> 暖风阵阵,晴空可见星
7°C a. t. 2°C
81% NW 5 m/s </td> </tr> </table>
邹韬奋虽然韬光养晦,亦当奋起而争(拜登永不为奴:h.2047.one) 我为了这个问题专门去学正则表达式...