批量替换MarkDown文档中指定的字符串
文章目录
前言
最近买了新电脑,各种配置要升级一下,几乎耗费了一个周末的时间,同时想将以前的博客文档导入过来,由于博客文档都是用Typora写的,其中图片也是存储在本地的。图片路径变化之后,文档中所有的图片都无法找到了,想着是不是可以通过工具解决这个事情
需要解决的问题
文档迁移之后,原有的文档中的图片都变成了这样
由于指定的文件路径找不到了,才会变成这样子。因此想到了,是不是可以通过文档替换的方式解决
解决方式和思路
方式
打开Typora编辑器,通过查找替换功能将图片路径替换成新的路径之后,图片可正常显示
那么问题来了,一个个手动替换?这也太原始了,于是就在这个背景下,写了一个批量替换文档中指定内容的代码,后来测试成功了,这里记录一下。
思路
第一步:递归找到所有文件;第二步:准备好源文件和目标文件;第三步:将匹配的内容替换,并将文件输出到指定的新文件
具体实现
在上面的思路上有着如下实现
递归找到所有文件
直接上代码,这个对我们来说应该不难,只是加入了一个是否是markdown文件的判断
private static Set<String> markDownFileSuffix = new HashSet<>();
static {
//只处理md文件
markDownFileSuffix.add("md");
}
/**
* 递归读取文件夹下的所有文件
*
* @param path
*/
public static void readFileInDir(String path, List<File> fileList) {
File f = new File(path);
//得到文件下的所有文件
File[] files = f.listFiles();
for (File file : files) {
if (file.isDirectory()) {
readFileInDir(file.getAbsolutePath(), fileList);
} else {
if (isMarkDownFile(file.getName())) {
fileList.add(file);
}
}
}
}
/**
* 判断是否是markdown文档
*
* @param fileName
* @return
*/
public static boolean isMarkDownFile(String fileName) {
boolean result = false;
String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
if (markDownFileSuffix.contains(suffix)) {
result = true;
}
return result;
}
准备好源文件和目标文件
由于有一个强迫症,并且为了避免之前的分类的文件夹乱掉,这里要求源文件是存在那个文件夹下,新的文件也一样存在那个分类的文件夹下,只是根目录不同而已。
因此在正式处理文件之前,先准备好目标文件才是硬道理
/**
* 根据原文件路径,创建目标文件
* @param sourceFile 源文件
* @param targetFileDir 目标文件根路径
* @return 创建好的目标文件
*/
public static File createTargetFile(File sourceFile,String targetFileDir){
//获取源文件绝对路径
String sourceFileAbsoluteName = sourceFile.getAbsolutePath();
//替换根路径,这里为了简单直接硬编码了,第一个参数可以有外部传递进来
String afterDealFileName = sourceFileAbsoluteName.replace("F:\\blog_doc", targetFileDir);
//截取出文件夹路径和文件名
int splitIndex = afterDealFileName.lastIndexOf("\\");
//文件夹路径
String dirPath = afterDealFileName.substring(0,splitIndex+1);
//文件名
String createFileName = afterDealFileName.substring(splitIndex+1);
log.info("准备创建的目标文件名为:{},存在于:{}文件夹",createFileName,dirPath);
//先递归创建文件夹
File dirFile = new File(dirPath);
if(!dirFile.exists()){
dirFile.mkdirs();
log.info("文件夹:{}创建完成",dirPath);
}
//创建目标文件
File targetFile = new File(dirPath,createFileName);
if(!targetFile.exists()){
try {
targetFile.createNewFile();
} catch (IOException e) {
log.error("文件:{}创建异常:{}",targetFile.getAbsolutePath(),e);
return null;
}
log.info("文件:{}创建完成",targetFile.getAbsolutePath());
}
//返回已经创建的文件
return targetFile;
}
将匹配的内容替换
将源文档中的指定内容替换,并输出到新的文档中
/**
* 开始替换文件中的内容
* @param sourceFile 源文件
* @param targetRegex 需要匹配的正则表达式
* @param toReplaceStr 需要替换成的字符串
*/
public static void replaceImgPath(File sourceFile, String targetRegex, String toReplaceStr) {
String regex = targetRegex;
String targetFileDir = "F:\\博客文档_新";//目标文件夹,处理之后的文件,都会写入到这个文件夹中,并且保证按照原有文件路径分类
BufferedReader reader = null;
BufferedWriter writer = null;
log.info("开始处理文件:{},最终文件会存在目录:{}中",sourceFile.getAbsoluteFile(),targetFileDir);
String fileName = sourceFile.getAbsoluteFile() + "/" + sourceFile.getName();
try {
//构造源文件读取器
reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile)));
//创建目标文件
File targetFile = createTargetFile(sourceFile,targetFileDir);
if(null == targetFile){
log.info("文件:{},目标文件创建失败,请手动操作",sourceFile.getAbsolutePath());
return;
}
//构造目标文件写入器
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile)));
String tempStr = "";
String regStr = regex;//有点多余,可以删除
Pattern pattern = Pattern.compile(regStr);
while ((tempStr = reader.readLine()) != null) {
tempStr += "\n"; //加上换行,避免格式错乱
Matcher matcher = pattern.matcher(tempStr);
if (matcher.find()) {//如果匹配上,进行替换
//按照指定正则替换
tempStr = tempStr.replaceAll(regex, toReplaceStr);
log.info("文件:{},替换后的字符串为:{}",sourceFile.getAbsoluteFile(), tempStr);
count++;//统计匹配的个数
}
writer.write(tempStr);
}
writer.flush();
} catch (Exception e) {
//记录异常
log.error("文件:{},字符替换异常,异常信息为:{},请手动操作", fileName,e);
return;
} finally {
//关闭流
try {
reader.close();
} catch (IOException e) {
log.error("文件:{},流关闭异常", fileName);
}
try {
writer.close();
} catch (IOException e) {
log.error("文件:{},流关闭异常", fileName);
}
}
}
测试情况
完整代码
package com.learn.sample;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* autor:liman
* createtime:2021-10-31
* comment: 读取已经编写的markdown文档,并对指定字符进行正则替换
*/
@Slf4j
public class ReplaceDocFileTools {
private static Set<String> markDownFileSuffix = new HashSet<>();
private static Integer count = 0;
static {
//只处理md文件
markDownFileSuffix.add("md");
}
public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis();
String path = "F:\\blog_doc\\博客文档";
//1.读取文件夹下的所有视频文件
List<File> fileList = new ArrayList<>();
readFileInDir(path, fileList);
List<String> fileNameList = fileList.stream().map(File::getName).collect(Collectors.toList());
log.info("得到的文件列表为:");
fileNameList.stream().forEach(t -> System.out.println(t));
log.info("待转换的文件个数为:{}",fileNameList.size());
String toRegexStr = "E{1}\\:\\\\blogPic\\b";
String toReplaceStr = "F:\\\\blog_doc\\\\blogPic";
fileList.stream().forEach(t -> replaceImgPath(t, toRegexStr, toReplaceStr));
long endTime = System.currentTimeMillis();
long costTime = endTime - startTime;
log.info("批量文件字符串替换完成,原始文件个数:{},总共替换字符串:{}个,总共耗时:{}ms",fileNameList.size(),count,costTime);
}
/**
* 递归读取文件夹下的所有文件
*
* @param path
*/
public static void readFileInDir(String path, List<File> fileList) {
File f = new File(path);
//得到文件下的所有文件
File[] files = f.listFiles();
for (File file : files) {
if (file.isDirectory()) {
readFileInDir(file.getAbsolutePath(), fileList);
} else {
if (isMarkDownFile(file.getName())) {
fileList.add(file);
}
}
}
}
/**
* 判断是否是markdown文档
*
* @param fileName
* @return
*/
public static boolean isMarkDownFile(String fileName) {
boolean result = false;
String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
if (markDownFileSuffix.contains(suffix)) {
result = true;
}
return result;
}
/**
* 开始替换文件中的内容
* @param sourceFile 源文件
* @param targetRegex 需要匹配的正则表达式
* @param toReplaceStr 需要替换成的字符串
*/
public static void replaceImgPath(File sourceFile, String targetRegex, String toReplaceStr) {
String regex = targetRegex;
String targetFileDir = "F:\\博客文档_新";//目标文件夹,处理之后的文件,都会写入到这个文件夹中,并且保证按照原有文件路径分类
BufferedReader reader = null;
BufferedWriter writer = null;
log.info("开始处理文件:{},最终文件会存在目录:{}中",sourceFile.getAbsoluteFile(),targetFileDir);
String fileName = sourceFile.getAbsoluteFile() + "/" + sourceFile.getName();
try {
//构造源文件读取器
reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile)));
//创建目标文件
File targetFile = createTargetFile(sourceFile,targetFileDir);
if(null == targetFile){
log.info("文件:{},目标文件创建失败,请手动操作",sourceFile.getAbsolutePath());
return;
}
//构造目标文件写入器
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile)));
String tempStr = "";
String regStr = regex;
Pattern pattern = Pattern.compile(regStr);
while ((tempStr = reader.readLine()) != null) {
tempStr += "\n"; //加上换行,避免格式错乱
Matcher matcher = pattern.matcher(tempStr);
if (matcher.find()) {//如果匹配上,进行替换
//按照指定正则替换
tempStr = tempStr.replaceAll(regex, toReplaceStr);
log.info("文件:{},替换后的字符串为:{}",sourceFile.getAbsoluteFile(), tempStr);
count++;
}
writer.write(tempStr);
}
writer.flush();
} catch (Exception e) {
//记录异常
log.error("文件:{},字符替换异常,异常信息为:{},请手动操作", fileName,e);
return;
} finally {
//关闭流
try {
reader.close();
} catch (IOException e) {
log.error("文件:{},流关闭异常", fileName);
}
try {
writer.close();
} catch (IOException e) {
log.error("文件:{},流关闭异常", fileName);
}
}
}
/**
* 根据原文件路径,创建目标文件
* @param sourceFile 源文件
* @param targetFileDir 目标文件根路径
* @return
*/
public static File createTargetFile(File sourceFile,String targetFileDir){
//获取源文件绝对路径
String sourceFileAbsoluteName = sourceFile.getAbsolutePath();
String afterDealFileName = sourceFileAbsoluteName.replace("F:\\blog_doc", targetFileDir);
//截取出文件夹路径和文件名
int splitIndex = afterDealFileName.lastIndexOf("\\");
//文件夹路径
String dirPath = afterDealFileName.substring(0,splitIndex+1);
//文件名
String createFileName = afterDealFileName.substring(splitIndex+1);
log.info("准备创建的目标文件名为:{},存在于:{}文件夹",createFileName,dirPath);
//先递归创建文件夹
File dirFile = new File(dirPath);
if(!dirFile.exists()){
dirFile.mkdirs();
log.info("文件夹:{}创建完成",dirPath);
}
//创建目标文件
File targetFile = new File(dirPath,createFileName);
if(!targetFile.exists()){
try {
targetFile.createNewFile();
} catch (IOException e) {
log.error("文件:{}创建异常:{}",targetFile.getAbsolutePath(),e);
return null;
}
log.info("文件:{}创建完成",targetFile.getAbsolutePath());
}
//返回已经创建的文件
return targetFile;
}
}
完整代码已贴出,运行情况如下所示
153个文档,替换1049处,耗时506毫秒
总结
一个简单的markdown文本替换工具