Batch replace string specified in MarkDown document
preface
Recently, I bought a new computer. It took almost a weekend to upgrade various configurations. At the same time, I want to import the previous blog documents. Because the blog documents are written in Typora, and the pictures are also stored locally. After the picture path changes, all the pictures in the document can't be found. I wonder if I can solve this problem through tools
Problems to be solved
After the document is migrated, the images in the original document become like this
This happens because the specified file path cannot be found. Therefore, I wonder if it can be solved by document replacement
Solutions and ideas
mode
Open the Typora editor and replace the picture path with a new path through the find and replace function. The picture can be displayed normally
So here's the problem. Replace them manually one by one? This is also the beginning. Under this background, I wrote a code to replace the specified content in the document in batch. Later, the test was successful. Here is a record.
thinking
Step 1: find all files recursively; Step 2: prepare the source and target files; Step 3: replace the matching content and output the file to the specified new file
Concrete implementation
The above ideas have the following implementation
Recursively find all files
It shouldn't be difficult for us to directly the code. We just added a judgment whether it is a markdown file
private static Set<String> markDownFileSuffix = new HashSet<>(); static { //Only md files are processed markDownFileSuffix.add("md"); } /** * Recursively read all files in the folder * * @param path */ public static void readFileInDir(String path, List<File> fileList) { File f = new File(path); //Get all files under the file File[] files = f.listFiles(); for (File file : files) { if (file.isDirectory()) { readFileInDir(file.getAbsolutePath(), fileList); } else { if (isMarkDownFile(file.getName())) { fileList.add(file); } } } } /** * Determine whether it is a markdown document * * @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; }
Prepare the source and destination files
Due to an obsessive-compulsive disorder and in order to avoid the confusion of the previously classified folders, it is required that the source files are stored in that folder, and the new files are also stored in that classified folder, but the root directory is different.
Therefore, it is the last word to prepare the target document before formally processing the document
/** * Create the target file according to the original file path * @param sourceFile source file * @param targetFileDir Destination file root path * @return Create a good target file */ public static File createTargetFile(File sourceFile,String targetFileDir){ //Get absolute path of source file String sourceFileAbsoluteName = sourceFile.getAbsolutePath(); //Replace the root path. Here, for simple and direct hard coding, the first parameter can be passed in externally String afterDealFileName = sourceFileAbsoluteName.replace("F:\\blog_doc", targetFileDir); //Intercept the folder path and file name int splitIndex = afterDealFileName.lastIndexOf("\\"); //Folder path String dirPath = afterDealFileName.substring(0,splitIndex+1); //file name String createFileName = afterDealFileName.substring(splitIndex+1); log.info("The target file name to be created is:{},Exist in:{}folder",createFileName,dirPath); //Create folder recursively first File dirFile = new File(dirPath); if(!dirFile.exists()){ dirFile.mkdirs(); log.info("folder:{}Creation complete",dirPath); } //Create target file File targetFile = new File(dirPath,createFileName); if(!targetFile.exists()){ try { targetFile.createNewFile(); } catch (IOException e) { log.error("file:{}Create exception:{}",targetFile.getAbsolutePath(),e); return null; } log.info("file:{}Creation complete",targetFile.getAbsolutePath()); } //Returns the created file return targetFile; }
Replace matching content
Replace the specified content in the source document and output it to a new document
/** * Start replacing contents in file * @param sourceFile source file * @param targetRegex A matching regular expression is required * @param toReplaceStr String to replace with */ public static void replaceImgPath(File sourceFile, String targetRegex, String toReplaceStr) { String regex = targetRegex; String targetFileDir = "F:\\Blog document_new";//The target folder and processed files will be written to this folder, and the classification will be guaranteed according to the original file path BufferedReader reader = null; BufferedWriter writer = null; log.info("Start processing file:{},The final file will exist in the directory:{}in",sourceFile.getAbsoluteFile(),targetFileDir); String fileName = sourceFile.getAbsoluteFile() + "/" + sourceFile.getName(); try { //Construct source file reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile))); //Create target file File targetFile = createTargetFile(sourceFile,targetFileDir); if(null == targetFile){ log.info("file:{},Failed to create target file, please operate manually",sourceFile.getAbsolutePath()); return; } //Construct target file writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile))); String tempStr = ""; String regStr = regex;//It's a little redundant and can be deleted Pattern pattern = Pattern.compile(regStr); while ((tempStr = reader.readLine()) != null) { tempStr += "\n"; //Add line breaks to avoid formatting confusion Matcher matcher = pattern.matcher(tempStr); if (matcher.find()) {//If it matches, replace it //Replace as specified tempStr = tempStr.replaceAll(regex, toReplaceStr); log.info("file:{},The replaced string is:{}",sourceFile.getAbsoluteFile(), tempStr); count++;//Count the number of matches } writer.write(tempStr); } writer.flush(); } catch (Exception e) { //Record exception log.error("file:{},Character replacement exception. The exception information is:{},Please operate manually", fileName,e); return; } finally { //Close flow try { reader.close(); } catch (IOException e) { log.error("file:{},Flow closing exception", fileName); } try { writer.close(); } catch (IOException e) { log.error("file:{},Flow closing exception", fileName); } } }
Test situation
Complete code
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: Read the written markdown document and perform regular replacement for the specified characters */ @Slf4j public class ReplaceDocFileTools { private static Set<String> markDownFileSuffix = new HashSet<>(); private static Integer count = 0; static { //Only md files are processed markDownFileSuffix.add("md"); } public static void main(String[] args) throws IOException { long startTime = System.currentTimeMillis(); String path = "F:\\blog_doc\\Blog document"; //1. Read all video files in the folder List<File> fileList = new ArrayList<>(); readFileInDir(path, fileList); List<String> fileNameList = fileList.stream().map(File::getName).collect(Collectors.toList()); log.info("The resulting file list is:"); fileNameList.stream().forEach(t -> System.out.println(t)); log.info("The number of files to be converted is:{}",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("Batch file string replacement completed, number of original files:{},Total replacement strings:{}, total time:{}ms",fileNameList.size(),count,costTime); } /** * Recursively read all files in the folder * * @param path */ public static void readFileInDir(String path, List<File> fileList) { File f = new File(path); //Get all files under the file File[] files = f.listFiles(); for (File file : files) { if (file.isDirectory()) { readFileInDir(file.getAbsolutePath(), fileList); } else { if (isMarkDownFile(file.getName())) { fileList.add(file); } } } } /** * Determine whether it is a markdown document * * @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; } /** * Start replacing contents in file * @param sourceFile source file * @param targetRegex A matching regular expression is required * @param toReplaceStr String to replace with */ public static void replaceImgPath(File sourceFile, String targetRegex, String toReplaceStr) { String regex = targetRegex; String targetFileDir = "F:\\Blog document_new";//The target folder and processed files will be written to this folder, and the classification will be guaranteed according to the original file path BufferedReader reader = null; BufferedWriter writer = null; log.info("Start processing file:{},The final file will exist in the directory:{}in",sourceFile.getAbsoluteFile(),targetFileDir); String fileName = sourceFile.getAbsoluteFile() + "/" + sourceFile.getName(); try { //Construct source file reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile))); //Create target file File targetFile = createTargetFile(sourceFile,targetFileDir); if(null == targetFile){ log.info("file:{},Failed to create target file, please operate manually",sourceFile.getAbsolutePath()); return; } //Construct target file writer 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"; //Add line breaks to avoid formatting confusion Matcher matcher = pattern.matcher(tempStr); if (matcher.find()) {//If it matches, replace it //Replace as specified tempStr = tempStr.replaceAll(regex, toReplaceStr); log.info("file:{},The replaced string is:{}",sourceFile.getAbsoluteFile(), tempStr); count++; } writer.write(tempStr); } writer.flush(); } catch (Exception e) { //Record exception log.error("file:{},Character replacement exception. The exception information is:{},Please operate manually", fileName,e); return; } finally { //Close flow try { reader.close(); } catch (IOException e) { log.error("file:{},Flow closing exception", fileName); } try { writer.close(); } catch (IOException e) { log.error("file:{},Flow closing exception", fileName); } } } /** * Create the target file according to the original file path * @param sourceFile source file * @param targetFileDir Destination file root path * @return */ public static File createTargetFile(File sourceFile,String targetFileDir){ //Get absolute path of source file String sourceFileAbsoluteName = sourceFile.getAbsolutePath(); String afterDealFileName = sourceFileAbsoluteName.replace("F:\\blog_doc", targetFileDir); //Intercept the folder path and file name int splitIndex = afterDealFileName.lastIndexOf("\\"); //Folder path String dirPath = afterDealFileName.substring(0,splitIndex+1); //file name String createFileName = afterDealFileName.substring(splitIndex+1); log.info("The target file name to be created is:{},Exist in:{}folder",createFileName,dirPath); //Create folder recursively first File dirFile = new File(dirPath); if(!dirFile.exists()){ dirFile.mkdirs(); log.info("folder:{}Creation complete",dirPath); } //Create target file File targetFile = new File(dirPath,createFileName); if(!targetFile.exists()){ try { targetFile.createNewFile(); } catch (IOException e) { log.error("file:{}Create exception:{}",targetFile.getAbsolutePath(),e); return null; } log.info("file:{}Creation complete",targetFile.getAbsolutePath()); } //Returns the created file return targetFile; } }
The complete code has been posted, and the operation is as follows
153 documents, 1049 replacements, 506 MS
summary
A simple markdown text replacement tool