前言:我是爱偷懒的勤快人
我们的口号是:
能用脑子解决的绝对不靠体力,能用电脑完成的绝对不靠脑子,能懒就懒,懒出奇迹
1.1:关于代码
你眼中的代码是什么,类的逻辑关系?方法的实现?不要忽略一个重要的本质:
代码是人可以读懂的可编译成可执行文件的字符串
,所以字符串很重要。 陆陆续续写了好几篇关于字符串的文章了,量变的基类往往带来质变的升华
今天我灵光一闪,便有此文。
1.2:本篇起由
今天写Flutter自定义组件,感觉写个StatefulWidget要罗里吧嗦一大堆 而且一开始都是差不多的,于是想来个一键生成,并放到gradle里 写着写着发现用上次的字符串替代类可以用,不用走平时的拼接了 果然有心栽花花不开,无心插柳柳成荫!本文主要的是对字符串的想法
2.模板替换解析器:TemplateParser
代码语言:javascript复制这是我再玩转字符串篇--替换的鬼斧神工的基础上进行完善的产物
1).可指定匹配符
2).优化了结构,使用Properties替换了HashMap,并使用配置文件
3).支持单文件和文件夹多文件替换
2.1:Properties的使用
代码语言:javascript复制在此之前,先说一下Properties的使用,感觉这个也挺好的,可以根据配置文件读成映射表 他继承自HashTable,只存放String到String的映射,下面来看一下他的用法 这里在项目最外面创建一个
config.properties
,写着键值对className=TolyWidget
public class Generation {
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
//读取属性文件a.properties
InputStream in = new BufferedInputStream(new FileInputStream("config.properties"));
prop.load(in); ///加载属性列表
Iterator<String> it = prop.stringPropertyNames().iterator();
while (it.hasNext()) {
String key = it.next();
System.out.println(key ":" prop.getProperty(key));
}
in.close();
}
}
---->[控制台输出]----
className:TolyWidget
这样就可以根据配置文件在代码中使用字符串的键值对了
2.2:解析类
代码语言:javascript复制最终的效果是可以通过配置文件的映射字符串,替换掉一个模板中的所有相应被标识部分 默认配置文件的位置在项目根部,名称
config.properties
,输出到模板的父目录同级的dest下
package generation;
import java.io.*;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TemplateParser {
/**
* 默认:$[X],自定义注意\转义
*/
private String symbol;
private Properties properties;
public TemplateParser() {
this("\$\[X\]", "config.properties");
}
public TemplateParser(String symbol, String propFilePath) {
this.symbol = symbol;
loadProperties(propFilePath);
}
/**
* 载入配置文件
* @param path 配置文件路径
*/
private void loadProperties(String path) {
properties = new Properties();
//读取属性文件a.properties
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(path));
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 解析字符串
*
* @param target 目标字符串
* @return 处理后的字符串
*/
public String parser(String target) {
String[] symbols = symbol.split("X");
Map<Integer, String> cutPos = new TreeMap<>();
String regex = symbols[0] "(?<result>.*?)" 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 result = properties.getProperty(v);
String substring = target.substring(0, k offset);
temp = substring.replace(src, result);
target = target.replace(substring, temp);
offset = result.length() - src.length();
}
return target;
}
/**
* 根据路径解析所有文件
* @param path 路径
*/
public void parserDir(String path) {
copyDir(path, path "-dest");//先拷贝一分
parserDir(new File(path "-dest"));
}
private void parserDir(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files == null) {
return;
}
for (File f : files) {
if (f.isDirectory()) {
parserDir(f);
} else {
saveFile(f, parserFile(file));
}
}
} else {
parserFile(file);
}
}
/**
* 解析一个文件
*
* @param path 路径
* @return 解析后的字符串
*/
public String parserFile(String path) {
File file = new File(path);
String result = parserFile(new File(path));
String out = file.getParentFile().getParentFile().getAbsolutePath() File.separator "dest" File.separator file.getName();
saveFile(new File(out), result);
return result;
}
/**
* 根据文件解析
*
* @param file 文件
* @return 解析后的字符串
*/
private String parserFile(File file) {
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());
}
//==========================文件相关操作===========================
/**
* 保存字符串文件
*
* @param file 文件
* @param content 字符串内容
*/
private void saveFile(File file, String content) {
ifNotExistMakeIt(file);
FileWriter fw = null;
try {
fw = new FileWriter(file);//写出到磁盘
fw.write(content);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 判断文件是否存在,不存在则创建它
*
* @param file
*/
private static void ifNotExistMakeIt(File file) {
if (file.exists()) {//2.判断文件是否存在
return;
}
File parent = file.getParentFile();//3.获取父文件
if (!parent.exists()) {
if (!parent.mkdirs()) {//4.创建父文件
return;
}
}
try {
file.createNewFile();//5.创建文件
} catch (IOException e) {
e.printStackTrace();
}
}
private static void ifNotExistMakeIt(String path) {
File file = new File(path);//1.创建文件
ifNotExistMakeIt(file);
}
/**
* 复制整个文件夹内容
*
* @param oldPath String 原文件路径
* @param newPath String 复制后路径
* @return boolean
*/
private void copyDir(String oldPath, String newPath) {
try {
(new File(newPath)).mkdirs(); //如果文件夹不存在 则建立新文件夹
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()) {//如果是子文件夹
copyDir(oldPath "/" file[i], newPath "/" file[i]);
}
}
} catch (Exception e) {
System.out.println("复制出错");
e.printStackTrace();
}
}
}
复制代码
里面有很多文件常用的操作,也可以抽出一个工具类收藏一下
3.Gradle里如何使用Java代码
关于Gradle的知识我有一篇专文:杂篇:一代版本一代神[-Gradle-]
3.1:Gradle里的task和路径获取
代码语言:javascript复制新建一个task在左边Gradle->other会有相应的任务,点一下就可以运行其中的代码 可以用
System.getProperty("user.dir")
获取当前项目的根目录
---->[app/build.gradle最后面]----
task generationTask() {//自定义一个任务
doFirst {
String root=System.getProperty("user.dir");
println("hello gradel:" root)
}
复制代码
3.2:Gradle中读取配置文件
代码语言:javascript复制由于Gradle中使用的是和Java兼容的Groovy语言,所以Java代码也是能运行的 这里在项目根文件下创建generation文件夹用来盛放配置文件以及模板和输出文件
task generationTask() {//自定义一个任务
doFirst {
String root=System.getProperty("user.dir");
println("hello gradel:" root)
Properties prop = new Properties();
//读取属性文件a.properties
InputStream is = new BufferedInputStream(new FileInputStream(root "/generation/config.properties"));
prop.load(is); ///加载属性列表
Iterator<String> itor = prop.stringPropertyNames().iterator();
while (itor.hasNext()) {
String key = itor.next();
System.out.println(key ":" prop.getProperty(key));
}
is.close();
}
}
复制代码
然后运行就可以看到获取的结果:
className:TolyWidget
3.3:使用
代码语言:javascript复制把刚才写的类全部拷到下面。说几个比较坑的点吧
1.Groovy中正则匹配不能用分组,我勒个去
2.符号$要用单引号,否则报错
3.函数不能重载,我勒个去
3.4:插件以及拆分文件导入
代码语言:javascript复制那么大一段写在一块不怎么雅观,拆一下呗,将插件逻辑全部抽到另一个文件了 也放在generation包里,这样整个流程所需要的东西都在一起,整个gradle只管用就行了 我们只需要在意模板和配置,两个都写好之后,轻轻一点,模板中需要替换的全部搞定
---->[使用方法,app/build.gradle]----
apply from: "./generation/generation.gradle"
3.5: generation.gradle全览
代码语言:javascript复制把这个拷过去用就行了,以后有什么写着比较烦而且没有技术含量的批量换一下呗。
// Create By 张风捷特烈(toly) in 2019.7.17 ---------
apply plugin: TemplateParserPlugin//声明使用插件
//----------------------------以下是插件部分--------------------------------
class TemplateParserPlugin implements Plugin<Project> {
//该接口定义了一个apply()方法,在该方法中,我们可以操作Project,
//比如向其中加入Task,定义额外的Property等。
void apply(Project project) {
//加载Extension
project.extensions.create("Config", Extension)
//使用Extension配置信息
project.task('TemplateParser') << {
String path = project.Config.root
new TemplateParser(path "config.properties")
.parserFile(path "template/StatefulTemplate.txt")
}
}
}
class Extension {//拓展参数
String root = System.getProperty("user.dir") "/generation/"
}
///----------------- 以下是解析类 -----------------
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TemplateParser {
/**
* 默认:$[X],自定义注意\转义
*/
private String symbol;
private Properties properties;
public TemplateParser() {
this('\$\[X\]', "config.properties");
}
public TemplateParser( String propFilePath) {
this('\$\[X\]',propFilePath);
}
public TemplateParser(String symbol, String propFilePath) {
this.symbol = symbol;
loadProperties(propFilePath);
}
/**
* 载入配置文件
* @param path 配置文件路径
*/
private void loadProperties(String path) {
properties = new Properties();
//读取属性文件a.properties
InputStream is = null;
try {
is = new BufferedInputStream(new FileInputStream(path));
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 解析字符串
*
* @param target 目标字符串
* @return 处理后的字符串
*/
public String parser(String target) {
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(0);
String v = matchStr.split(symbols[0])[1].split(symbols[1])[0]
cutPos.put(matcher.end(), v);
}
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 result = properties.getProperty(v);
String substring = target.substring(0, k offset);
temp = substring.replace(src, result);
target = target.replace(substring, temp);
offset = result.length() - src.length();
}
return target;
}
/**
* 根据路径解析所有文件
* @param path 路径
*/
public void parserDir(String path) {
copyDir(path, path "-dest");//先拷贝一分
_parserDir(new File(path "-dest"));
}
private void _parserDir(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files == null) {
return;
}
for (File f : files) {
if (f.isDirectory()) {
parserDir(f);
} else {
saveFile(f, parserFile(file));
}
}
} else {
parserFile(file);
}
}
/**
* 解析一个文件
*
* @param path 路径
* @return 解析后的字符串
*/
public String parserFile(String path) {
File file = new File(path);
String result = _parserFile(new File(path));
String out = file.getParentFile().getParentFile().getAbsolutePath() File.separator "dest" File.separator file.getName();
saveFile(new File(out), result);
return result;
}
/**
* 根据文件解析
*
* @param file 文件
* @return 解析后的字符串
*/
private String _parserFile(File file) {
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());
}
//=============================================文件相关操作=============================================
/**
* 保存字符串文件
*
* @param file 文件
* @param content 字符串内容
*/
private void saveFile(File file, String content) {
ifNotExistMakeIt(file);
FileWriter fw = null;
try {
fw = new FileWriter(file);//写出到磁盘
fw.write(content);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 判断文件是否存在,不存在则创建它
*
* @param file
*/
private static void ifNotExistMakeIt(File file) {
if (file.exists()) {//2.判断文件是否存在
return;
}
File parent = file.getParentFile();//3.获取父文件
if (!parent.exists()) {
if (!parent.mkdirs()) {//4.创建父文件
return;
}
}
try {
file.createNewFile();//5.创建文件
} catch (IOException e) {
e.printStackTrace();
}
}
private static void ifNotExistMakeIt(String path) {
File file = new File(path);//1.创建文件
ifNotExistMakeIt(file);
}
/**
* 复制整个文件夹内容
*
* @param oldPath String 原文件路径
* @param newPath String 复制后路径
* @return boolean
*/
private void copyDir(String oldPath, String newPath) {
try {
(new File(newPath)).mkdirs(); //如果文件夹不存在 则建立新文件夹
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()) {//如果是子文件夹
copyDir(oldPath "/" file[i], newPath "/" file[i]);
}
}
} catch (Exception e) {
System.out.println("复制整个文件夹内容操作出错");
e.printStackTrace();
}
}
}