前言
前不久,筆者在工作中遇到一項任務,內容爲實現使用配置文件中元素的註釋或者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;
}
}
結束語
算法之所以有趣,是因爲算法能將人的智慧賦予給計算機。