0. 起因
最近龙珠又更新了,不知道各位小伙伴知不知道,今日一气之下,将所有更新下载了下来,可是问题来了,下下来的文件,名字中都会有网站的信息,如:“[龙珠超][40][红旅首发www.hltm.cc][GB][720P][MP4][异域字幕组].mp4”,而小生只想要这样的命名:“龙珠超-01.mp4”。
于是乎,就有了如下问题:如何批量重命名呢?
环境:win10_x64, git bash(windows cmd太难用了), sublime text 3
过程很清晰了:列出所有mp4文件来,找到它是第几集,把名字拼起来,改名,完毕!
那就开工吧!
1. Python
python脚本似乎很好写呢,那就试试呗
# -*- coding: utf8 -*-
import os
import os.path
import re
import sys
usage = '''\
Usage: newname\
'''
if len(sys.argv) < 2:
print usage
exit(1)
# ---------------------------
FILE_NAME = ' '.join(sys.argv[1:]) # Dragon ball super 龙珠超
FILE_TYPE = '.mp4'
pwd = os.getcwd()
print 'pwd is:', pwd
files = [file for file in os.listdir(pwd) if os.path.isfile(file) and os.path.splitext(file)[1] == FILE_TYPE]
regexp = ur'[0-4][0-9]'
fails = []
for file in files:
num = re.search(regexp, file)
if num:
newname = "%s-%s%s" % (FILE_NAME, num.group(), FILE_TYPE)
print "%s: %s --> %s" % (num.group(), file, newname)
os.rename(file, newname)
else:
fails.append(file)
if not fails:
exit(0)
print "\n**************** fails ****************"
for file in fails:
print file
exit(1)
运行:python rename.py 龙珠超
这是最后的样子了,之前写的时候,小生直接把“龙珠超”写在了代码里,python会报windows errno: 123的错误,但把encoding改成cp936又会报无法识别汉字的问题,最后只能将就把“龙珠超”当参数传进来了。
2. PHP
php是世界上最好的语言,伟哉我大php!!!
如上,用python会因为编码问题而困扰,所以就想到了用世界上最好的编程语言试试:
<?php
define("FILE_NAME", "龙珠超");
define("FILE_EXT", "mp4");
$pwd = getcwd();
if (!$pwd) {
echo "Getcwd error!";
exit(1);
}
echo "pwd is: $pwd\n";
$handler = opendir($pwd);
if ($handler === false) {
echo "Opendir error!";
exit(1);
}
$fails = [];
while ($file = readdir($handler)) {
if ($file === false) {
echo "Read dir error!";
exit(1);
}
if (pathinfo($file, PATHINFO_EXTENSION) != FILE_EXT) {
continue;
}
preg_match('/[0-4][0-9]/', $file, $num);
if (count($num) == 0) {
$fails[] = $file;
continue;
}
$newname = FILE_NAME . "-$num[0]." . FILE_EXT;
echo "$file --> $newname\n";
if (rename($file, $newname) == false) {
$fails[] = $file;
}
}
closedir($handler);
if ($fails) {
echo "\n" . '************ fails ************' . "\n";
foreach ($fails as $file) {
echo "$file\n";
}
exit(1);
}
运行:php rename.php
很不幸,又报windows 123错了,郁闷了……
3. Golang
上面两个都跪了,用Google的语言试试吧
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
)
const (
FILE_NAME = "龙珠超"
FILE_EXT = ".mp4"
)
func main() {
pwd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
fileinfos, err := ioutil.ReadDir(pwd)
if err != nil {
log.Fatal(err)
}
re := regexp.MustCompile(`[0-4][0-9]`)
fails := []string{}
for _, info := range fileinfos {
if info.IsDir() {
continue
}
if filepath.Ext(info.Name()) != FILE_EXT {
continue
}
newname := fmt.Sprintf("%v-%v%v", FILE_NAME, re.FindString(info.Name()), FILE_EXT)
fmt.Printf("%v --> %v\n", info.Name(), newname)
if err := os.Rename(info.Name(), newname); err != nil {
log.Println(err)
fails = append(fails, info.Name())
}
}
if len(fails) > 0 {
fmt.Println("****************** fails ******************")
for _, file := range fails {
fmt.Println(file)
}
os.Exit(1)
}
}
运行:go run rename.go
哈,一次就过,Google语言确实牛逼!
4. C
想想都写了这么多了,为啥不试试万能的C语言呢?
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define FILE_NAME "龙珠超"
#define FILE_EXT ".mp4"
#define Free(ptr) if(ptr){free(ptr);ptr=NULL;}
int endwith(const char* str, const char* end) {
size_t l_str = strlen(str);
size_t l_end = strlen(end);
if (l_str < l_end) {
return 0;
}
return !strcmp(str+(l_str-l_end), end);
}
typedef struct {
int start, end;
} range_t;
int find_num_seq(const char* src, size_t size, size_t n, range_t ranges[], size_t end) {
range_t range;
size_t i, cnt, seq_size, len = strlen(src);
if (end > len) {
end = len;
}
for (i = cnt = seq_size = 0; i < end && cnt < n; i++) {
if (isdigit(src[i])) {
if (seq_size == 0) {
range.start = i;
}
range.end = i+1;
seq_size++;
} else {
if (seq_size == size) {
ranges[cnt].start = range.start;
ranges[cnt].end = range.end;
cnt++;
}
seq_size = 0;
}
}
return cnt == n;
}
int main() {
char* pwd = NULL;
DIR* dir = NULL;
struct dirent* file = NULL;
int err = 0;
if ((pwd = getcwd(NULL, _MAX_PATH)) == NULL) {
err = errno;
printf("getcwd error!\n");
goto END;
}
printf("pwd: %s\n", pwd);
if ((dir = opendir(pwd)) == NULL) {
err = errno;
printf("opendir error!\n");
goto END;
}
while (1) {
if ((file = readdir(dir)) == NULL) {
err = errno;
if (err) {
printf("readdir error!\n");
goto END;
} else {
break;
}
}
if (endwith(file->d_name, FILE_EXT)) {
int i;
range_t range;
if (find_num_seq(file->d_name, 2, 1, &range, -1)) {
char* newname = NULL;
char* num = (char*)malloc((range.end-range.start+1) * sizeof(char));
if (num == NULL) {
err = errno;
printf("malloc error!\n");
goto END;
}
memset(num, 0, sizeof(num));
memcpy(num, file->d_name+range.start, range.end-range.start);
newname = (char*)malloc((strlen(FILE_NAME) + strlen(num) + strlen(FILE_EXT) + 8) * sizeof(char));
memset(newname, 0, sizeof(newname));
sprintf(newname, "%s-%s%s", FILE_NAME, num, FILE_EXT);
printf("%s --> %s\n", file->d_name, newname);
if (rename(file->d_name, newname) != 0) {
err = errno;
Free(newname);
Free(num);
printf("rename error!\n");
goto END;
}
Free(newname);
Free(num);
} else {
printf("fail: %s\n", file->d_name);
}
}
Free(file);
}
END:
if (dir != NULL) {
closedir(dir);
dir = NULL;
}
Free(pwd);
if (err) {
printf("errno[%d]: %s\n", err, strerror(err));
}
return err;
}
运行:gcc rename.c -o rename && ./rename
也报错了,与众不同的是,报的errno是22 Invalid argument。这就不应该了吧,c这么万能的语言怎么能解决不了这种小儿科呢?
肯定是哪里出错了!可是是哪里呢?我试着用记事本打开rename.c并保存居ansi编码,然后再运行,OK了!!!原来是sublime保存utf8编码不适用于windows!!!
那来验证一下:
5. Java
import java.io.File;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Rename {
private static final String FILE_NAME = "龙珠超"; // Dragon ball super
private static final String FILE_EXT = ".mp4";
private static String getExt(String file) {
if (file.lastIndexOf(".") > 0 && file.lastIndexOf(".") < file.length()-1) {
return file.substring(file.lastIndexOf("."));
}
return "";
}
public static void main(String[] args) {
String pwd = System.getProperty("user.dir");
File[] files = (new File(pwd)).listFiles();
ArrayList<String> fails = new ArrayList<String>();
Pattern pattern = Pattern.compile("[0-4][0-9]");
for (File file: files) {
if (file.isFile() && getExt(file.getName()).equals(Rename.FILE_EXT)) {
System.out.println(file.getName());
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
String newname = Rename.FILE_NAME + "-" + matcher.group() + Rename.FILE_EXT;
System.out.format("%s --> %s", file.getName(), newname);
try {
file.renameTo(new File(newname));
} catch (Exception e) {
e.printStackTrace();
fails.add(file.getName());
}
} else {
fails.add(file.getName());
}
}
}
if (fails.size() > 0) {
System.err.println("************* fails *************");
for (String file: fails) {
System.err.println(file);
}
System.exit(1);
}
}
}
运行: javac Rename.java && java Rename
在保存为utf8时编译通不过,改为ansi时正常运行。
6. Bash
细细想来,直接写个bash脚本不就OK了吗?为啥要启用编程语言呢?
#/bin/bash
FILE_NAME="龙珠超"
FILE_EXT="mp4"
files=(`ls`)
for file in ${files[@]}; do
if [[ "${file##*.}" = "$FILE_EXT" ]]; then
num=`expr match "$file" '.*\([0-4][0-9]\).*'`
newname="${FILE_NAME}-${num}.${FILE_EXT}"
echo $file "-->" $newname
mv "$file" "$newname"
fi
done
运行:./rename.sh
一切OK!!!
俗话说,天做孽,犹可恕,自做孽,不可活呀!
反思:
为什么一开始想到的不是bash脚本,而是用python这类脚本去解决呢?
处理什么样的问题,用什么样的工具,用最简单,最切合实际的工具去解决它最擅长的领域。Python也许还有几分道理,但如PHP本是用来解决网站问题的,Golang是解决服务器问题的,C用的最没道理,因为一个这种小问题,完全找不到用它的理由。
另外,亦可能是因为windows平台,直接就掠过bash脚本(小生不会用bat),没意识到一直用的是git bash。windows的cp936编码
直接导致Sublime下岗,Python罢工,除Golang和Bash之外要指定保存编码格式为ansi。之后再处理类似问题时须注意。