前不久,笔者在工作中遇到一项任务,内容为实现使用配置文件中元素的注释或者key值来找到该元素,然后对value值进行修改。需要操作的配置文件有3种,properties、config和xml。其中对properties定位和修改元素的功能实现起来非常简单,不值一提。config和xml两种文件操作起来稍微麻烦点,笔者现将实现对config文件按照元素的注释或者key值来定位和修改元素的算法和代码公布。
欢迎各位程序员或者算法爱好者阅读交流,笔者QQ:1072334275,如果问题或者优化意见请QQ联系我。
首先,config配置文件的格式如下图:由注释和元素内容组成,注释写在对应元素的上部,元素上下一行分别是左右中括号。当然,注释可能不止一行,元素也可能没有注释。
当前要实现的功能是:无论通过元素的注释或者key值的某段字符串都可以找到该元素,并且得到元素的注释与完整的key、value键值内容,也能修改改元素的value值。
我们的Java语言在读取文件内容时是按行来逐行读取的,我们可以通过循环来逐行的得到文件的每一行内容。但是在该配置文件中,注释、key值、value值均在不同的相邻行中,因此我们会在不同轮数的循环中得到他们的内容。因此,我们需要在不同轮的循环中建立一种通信机制,让前面的循环告诉后面的循环:你正则得到行是什么内容,需要执行什么动作。这种通信机制,在Java语言中很好实现,我们可以通过变量的自增和对变量值的判断来进行。例如,如果上轮循环中检测到了当前循环读取的行是正确的key值,我们就可以让某个变量进行一次变更,后面的循环读取到这种变更时,就会知道本轮循环要读取的行是value值,需要进行执行存储动作。
下面,我通过具体的算法和代码来为你演示这种编程的思想和方法。
一、元素查找
需要实现的功能是元素可以通过注释或者元素的key值来进行查找,并且找到元素后带出元素的注释、key值、value值等全部内容。具体算法为:
1、使用I/O流逐行读取文件,方法很简单,网上有现成的代码。
2、定义两个变量,i变量作为循环之间进行相互通信的媒介,每次循环通过检查i值判断当前读取到的行是什么内容,需要执行什么动作。getNote变量也是循环间进行相互通信的媒介,当用户输入key值进行查找时,需要判断程序是否先读取到了注释,或者某些元素没有注释直接读取到了元素内容,以便作为后面的循环时是否需要作注释舍弃动作的依据。
3、判断用户输入的是注释还是key值,以用户输入的内容是否包含汉字作为判断依据。
4、根据用户输入的内容类型不同,需要用不同的算法进行计算。
如果用户输入的是注释,则按照以下方法进行:
(1)读取到某一行时先判断其是否为注释,以该行的第一个字符是否为“#”作为依据。如果是注释,将该行先存储起来,再进行判断是否包含用户输入的字符串。
(2)如果在上步中找到当前读取的行包含用户输入的字符串,则说明已经找到我们需要寻找的内容。此时,我们需要将i值的自增一次,来作为已经找到正确注释的标志。
(3)如果当前读取的行不是注释,说明注释内容读取结束,已经开始读取到元素内容,。此时我们需要判断i的值是否自增过,来判断前面读取的注释是否已经找到用户输入的内容。
(4)如果i值自增过一次,说明前面读取的注释是用户需要寻找的元素的注释,此时我们需要通过判断该行的第一个字符是不是“[”来判断此行是不是元素内容的开始。如果如果此行是元素内容的开始,则将i值再次自增,告知后面的循环,即将读取元素key值了。
(5)如果i值出现两次自增,说明本轮循环读取的行为元素的key值,需要进行存储。存储后,i值继续自增,告诉后面的循环,即将读取元素的value值。
(6)如果i值出现了三次自增,说明此行是元素的value值,存储后将结果返回。
如果用户输入的是key值,按照以下算法进行:
(1)读取到某一行时先判断其是否为注释,以该行的第一个字符是否为“#”作为依据。如果是注释,将该行先存储起来。getNote的值进行自增一次,告知后面的循环,前面的循环已经存储了注释内容。
(2)如果此行不是注释,则通过判断当前循环读取的行的第一个字符是否是“[”,来判断此行是不是元素内容的开始。,是的话i值自增一次。
(3)如果此行不是注释,检测i值是否自增过一次,是的话则表示前面读取到元素的开始符号,此次循环读取的行为元素的key值,需要判断本次读取的key值是否包含用户输入的内容,如果包含的话则将i值再次自增即可。不包含的话将i的值还原为0,并且通过检测getNote值是否出现一次或者多次自增来判断是否存储过注释,如果存储过注释的话,存储的注释也需要清空。
(4)如果此行不是注释,检测i值是否自增过两次,如果自增过两次,说明上轮循环中已经将key值校验无误,本轮循环读取到的是正确的value值,需要存储。
二、元素修改
笔者打字打累了,修改算法就省略一下吧,请读者阅读下面的程序代码和代码的注释,来自己体会一下计算过程。我写的程序注释比较详细,相信聪明的读者能够轻易读懂。
public class OperateConfig {
//此方法用于在config文件中以注释或者元素的key值查找元素,其中confFilePath表示文件路径,
//seekContent表示用于查找元素的关键字,可以是元素key值的一部分,也可以是元素注释的一部分
public static Map<String,String> getConfigElement(String confFilePath,String seekContent) {
File confFile = new File(confFilePath);
Map<String,String> element=new HashMap();
//i变量作为循环之间进行相互通信的媒介,每次循环通过检查i值判断当前读取到的行是什么内容,
//是要找的key值,还是要找的value值
int i=0;
//getNote变量也是循环间进行相互通信的媒介,当用户输入key值进行查找时,需要判断程序是否先读取到
//了注释,或者某些元素没有注释直接读取到了元素内容,以便作为后面的循环时是否需要作注释舍弃动作
//的依据
int getNote=0;
String line = "";
try {
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(confFile),"GB2312"));
while((line=br.readLine()) != null) {
line=line.trim();
if(!line.equals("")) {
//如果用户输入的是注释,我们需要以注释来进行查找。isCNChar()方法在写程序的最下面
if(isCNChar(seekContent)) {
//如果当前读取到的行是注释,isNote()方法见程序最下面
if(isNote(line)) {
//先把注释存储起来,因为注释不止一行,我们不确定用户输入的是哪一行注释,所以先进性存储
element.put("note", element.get("note")+line);
//找到正确的注释,i自增,表示注释找到了
if(line.contains(seekContent)) {
//如果找到正确的注释,以i自增作为标志,告诉后面的循环,已经找到正确的注释
i++;
}
continue;
}else {
//如果在遍历注释时没有找到正确的注释,将已经存储的注释舍弃
if(i==0) {
element.put("note","");
}
//如果i值出现自增,说明在遍历注释时已经找到正确的注释,此时我们需要在判断当前行
//不是元素的开始符合,即“[”
String oneChar=line.substring(0, 1);
if(i==1) {
//如果此行是元素的起始符,i再次自增,告诉后面的循环,即将读取key值
if(oneChar!=null&&oneChar.equals("[")) {
i++;
continue;
}
}
if(i==2) {
//如果i值自增到了2,说明此行是key值,存储后i再次自增,告诉后面的循环即将读取value值
element.put("key", line);
i++;
continue;
}
//如果i自增到了3,说明此行是value值
if(i==3) {
element.put("value", line);
i=0;
return element;
}
}
//如果用户输入的不是注释,是key值
}else {
//如果这行是注释,先存储起来
if(isNote(line)) {
element.put("note", element.get("note")+line);
getNote++;;
}else if(getNote>0){
String oneChar=line.substring(0, 1);
//说明元素注释行依据结束,下一行将开始读取元素内容,以i自增1作为标志
if(oneChar!=null&&oneChar.equals("[")) {
i++;
continue;
}
//如果i值出现自增,表示本轮循环读取的就是元素的key值,需要判断key值是不是需要找的
if(i==1) {
//如果key值正确,将key存储,i再次自增,告诉后面的循环,当前元素正是我们要找的
if(line.contains(seekContent)) {
element.put("key", line);
i++;
continue;
}else {
//如果key值不正确,则说明当前的元素不是我们要找的,将已经存储的注释舍弃,i值和getNote还原
i=0;
getNote=0;
element.put("note","");
continue;
}
}
//如果i值已经自增到了2,说明上轮循环key值校验无误,可以存储本轮循环的value值了
if(i==2) {
element.put("value", line);
i=0;
return element;
}
}else {
String oneChar=line.substring(0, 1);
if(oneChar!=null&&oneChar.equals("[")) {
i++;
continue;
}
if(i==1) {
if(line.contains(seekContent)) {
element.put("key", line);
i++;
continue;
}else {
i=0;
continue;
}
}
if(i==2) {
element.put("value", line);
i=0;
return element;
}
}
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return element;
}
//准备方法,实现对config文件按照配置项进行修改,以key值进行查找和修改
public void updateConfig(String key,String value) {
// TODO Auto-generated method stub
File file = new File("E:/config/LBSConfig");
BufferedReader reader = null;
List<Map> elementList=new ArrayList();
StringBuffer sbf = new StringBuffer();
String line=System.getProperty("line.separator");//平台换行!
PrintWriter pw=null;
try {
reader = new BufferedReader(new InputStreamReader(
new FileInputStream(file), "GB2312"));
String tempStr;
int i=0;
int y=0;
Map<String,String> element=new HashMap();
while ((tempStr = reader.readLine()) != null) {
//去除空格
tempStr=tempStr.trim();
if(tempStr!=null) {
if(tempStr.equals("")) {
sbf.append(tempStr+line);//保留换行信息
}else {
//截取第一个字符
String oneChar=tempStr.substring(0, 1);
//判断是不是注释
if(oneChar!=null&&oneChar.equals("#")) {
sbf.append(tempStr+line);
}
//判断是不是元素的开始
if (oneChar!=null&&oneChar.equals("[")) {
i++;
sbf.append(tempStr+line);
continue;
}
//判断是不是元素的结尾
if (oneChar!=null&&oneChar.equals("]")){
//创建新对象,用来存储元素
i=0;
sbf.append(tempStr+line);
continue;
}
//判断是不是key
if(i==1) {
if(tempStr.equals(key)) {
y++;//通知后面的循环,已经找到需要修改的元素
}
i++;
sbf.append(tempStr+line);
continue;
}
if(i==2) {
i++;
sbf.append(value+line);//将值换成传入的value存储
y=0;//y重新归于0
continue;
}
}
}
}
pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"GB2312")));
pw.println(sbf);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if(pw!=null) {
pw.close();
}
}
}
//准备方法,判断读到的行是不是注释
public static boolean isNote(String line) {
String ch=line.substring(0,1);
if(ch.equals("#")) {
return true;
}else {
return false;
}
}
//准备方法,判断读到的行是否包含汉字
public static boolean isCNChar(String line){
boolean booleanValue = false;
for(int i=0; i<line.length(); i++){
char c = line.charAt(i);
if(c> 128){
booleanValue = true;
break;
}
}
return booleanValue;
}
}
算法之所以有趣,是因为算法能将人的智慧赋予给计算机。