This article suggests that

Problem of 1.1.

Today I encountered a problem in how to specify batch substitution for certain strings. Scenario: For example, the following markdown paragraph is always a long one when there are many fixed links when writing an article. Especially in the form, feel to collapse ah, and in case one day to change the web address, one by one do not change crazy. No, we have to figure out a way. After all, we can’t beat the “Hello World” code.

Padding is a control that generates an inner margin. [Padding] (https://juejin.cn/user/149189281194766) [Container] (https://juejin.cn/user/149189281194766) has a Padding properties, details visible! [Padding](https://juejin.cn/user/149189281194766)Copy the code

1.2. The idea

I would like to write the following, with $[XXX] to me automatically change to the corresponding link, look cool, so bite a tooth, make a wave, once and for all. The end result is that all markdown files in a folder are converted with one click.

$[Padding] $[Container] has a Padding property, which is visible in $[Padding].Copy the code

2. Implement

2.1: String matching

$[XXX] = $[XXX]

String test="Padding is a control that generates an inside margin \n" +
        $[Padding]\n" +
        "$[Container] has a padding property, \n" +
        "$[Padding]";
parse(test);

private static void parse(String target) {
    String regex = "\\$\\[(?<result>.*?)\\]";
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(target);
    while (matcher.find()) {
        String matchStr=matcher.group("result"); }} ---->[Print result]---- Padding Container PaddingCopy the code

2.2: What then

The replace method took some time, but it didn’t work. ReplaceAll could do if there were too few links, but a lot of them were a little scary. Then when I saw Matcher’s end() method, I was happy

private static void parse(String target) { Map<Integer,String> cutPos=new TreeMap<>(); // Breakpoint mapping String regex ="\\$\\[(?<result>.*?)\\]";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(target);
        while (matcher.find()) {
            String matchStr=matcher.group("result"); catPos.put(matcher.end(),matchStr); // Put mapping}}Copy the code

2.3: Final processing

Debugging for a while, grope for to a method: breakpoint string can be divided into two and a half, the front treatment before and after the period of together, thus the first with respect to ok Then process of string, there is a problem: is the index of the breakpoint to offset, because the original string have changed, of course this also I won’t smart

private static void parse(String target) {
    Map<Integer,String> cutPos=new TreeMap<>();
    Map<String,String> urlMap=new HashMap<>();
    urlMap.put("Padding"."https://juejin.cn/user/149189281194766 urlMap.put("Container","https://juejin.cn/user/149189281194766"); String regex = "\\$\\[(?<result>.*?)\\]"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(target); while (matcher.find()) { String matchStr=matcher.group("result"); System.out.println(matchStr); cutPos.put(matcher.end(),matchStr); } Iterator
      
       > iterator = cutPos.entrySet().iterator(); String temp; int offset=0; while (iterator.hasNext()){ Map.Entry
       
         e = iterator.next(); int k = e.getKey(); String v=e.getValue(); String src="
       ,>
      $["+v+"]"; String dest= "! ["+v+"]"+"("+urlMap.get(v)+")"; String substring = target.substring(0,k+offset); temp = substring.replace(src, dest); target = target.replace(substring, temp); offset+=dest.length()-src.length(); }}Copy the code

And then we run, and we’re done. Okay, the text is about to begin


3. Optimization and encapsulation

All right, we have the core technology, all we need is a shell

3.1: Parser class

For string parsing, note that you can customize symbols, but remember to escape

Public class Parser {/** * default :$[X], custom note \\ escape */ private String symbol; publicParser() {
        this.symbol = "\\$\\[X\\]"; } public Parser(String symbol) { this.symbol = symbol; } /** ** parse string * @param target target string * @param matchMap matchMap * @returnPublic String parse(String target, Map<String,String> matchMap) {String[] symbols = symbol.split("X");

        Map<Integer,String> cutPos=new TreeMap<>();
        String regex = symbols[0]+"(? 
      
       .*?) "
      +symbols[1];
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(target);
        while (matcher.find()) {
            String matchStr=matcher.group("result");
            cutPos.put(matcher.end(),matchStr);
        }

        Iterator<Map.Entry<Integer, String>> iterator = cutPos.entrySet().iterator();
        String temp;
        int offset=0;
        while (iterator.hasNext()){
            Map.Entry<Integer, String> e = iterator.next();
            int k = e.getKey();
            String v=e.getValue();
            String src="$["+v+"]";
            String dest= ! "" ["+v+"]"+"("+matchMap.get(v)+")";
            String substring = target.substring(0,k+offset);
            temp = substring.replace(src, dest);
            target = target.replace(substring, temp);
            offset+=dest.length()-src.length();
        }

        returntarget; }}Copy the code

3.2: File operations

For my upcoming open source Flutter component collection project, there are many other components or property links in each file, so I need to find a solution. Otherwise, one by one will feel impractical and confusing, which will lead to the bad feeling of writing code, so I need to batch file operation

public String parserFile(String path, Map<String,String> matchMap)
    returnparserFile(new File(path),matchMap); } /** * param matchMap matches the mapping * @return
 */
public String parserFile(File file, Map<String,String> matchMap){
    InputStream is=null;
    StringBuilder sb = new StringBuilder();
    try {
        is= new FileInputStream(file);
        int len=0;
        byte[] buffer=new byte[1024];
        while((len=is.read(buffer))! =-1){ sb.append(new String(buffer,0,len)); } } catch (Exception e) { e.printStackTrace(); }finally { try {if (is != null) {
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return parser(sb.toString(),matchMap);
}
Copy the code

3.3: Folder batch file operation

OK, finished work, now can be happy to write md file, string operation is really fun if what needs to be replaced in the future, according to this idea to OK, avoid unnecessary labor pay.

public void parserDir(String path, Map<String, String> matchMap) {
    copyDir(path, path + "-src"); ParserDir (new File(path), matchMap); } private void parserDir(File file, Map<String, String> matchMap) {if (file.isDirectory()) {
        File[] files = file.listFiles();
        if (files == null) {
            return;
        }
        for (File f : files) {
            if (f.isDirectory()) {
                parserDir(f, matchMap);
            } else {
                if (f.getName().endsWith(".md")) { handleFile(matchMap, f); }}}}else{ parserFile(file, matchMap); } } private void handleFile(Map<String, String> matchMap, File f) { FileWriter fw = null; String result = parserFile(f, matchMap); try { fw = new FileWriter(f); // Write to disk fw.write(result); } catch (IOException e) { e.printStackTrace(); } finally { try {if(fw ! = null) { fw.close(); } } catch (IOException e) { e.printStackTrace(); }}} /** * Copy the contents of the entire folder ** @param oldPath String Original file path * @param newPath String Copied path * @returnboolean */ public void copyDir(String oldPath, String newPath) { try { (new File(newPath)).mkdirs(); // If the folder does not exist, create a new folder File a = new File(oldPath); String[] file = a.list(); File temp = null;if (file == null) {
            return;
        }
        for (int i = 0; i < file.length; i++) {
            if (oldPath.endsWith(File.separator)) {
                temp = new File(oldPath + file[i]);
            } else {
                temp = new File(oldPath + File.separator + file[i]);
            }
            if (temp.isFile()) {
                FileInputStream input = new FileInputStream(temp);
                FileOutputStream output = new FileOutputStream(newPath + "/" +
                        (temp.getName()).toString());
                byte[] b = new byte[1024 * 5];
                int len;
                while((len = input.read(b)) ! = -1) { output.write(b, 0, len); } output.flush(); output.close(); input.close(); }if(temp.isdirectory ()) {// If it is a subfolder copyDir(oldPath +"/" + file[i], newPath + "/" + file[i]);
            }
        }
    } catch (Exception e) {
        System.out.println("Error copying entire folder contents"); e.printStackTrace(); }}Copy the code