在循環之間建立通信,實現對一種config配置文件的元素定位與修改

前言

前不久,筆者在工作中遇到一項任務,內容爲實現使用配置文件中元素的註釋或者key值來找到該元素,然後對value值進行修改。需要操作的配置文件有3種,properties、config和xml。其中對properties定位和修改元素的功能實現起來非常簡單,不值一提。config和xml兩種文件操作起來稍微麻煩點,筆者現將實現對config文件按照元素的註釋或者key值來定位和修改元素的算法和代碼公佈。

歡迎各位程序員或者算法愛好者閱讀交流,筆者QQ:1072334275,如果問題或者優化意見請QQ聯繫我。

功能要求

首先,config配置文件的格式如下圖:由註釋和元素內容組成,註釋寫在對應元素的上部,元素上下一行分別是左右中括號。當然,註釋可能不止一行,元素也可能沒有註釋。
當前要實現的功能是:無論通過元素的註釋或者key值的某段字符串都可以找到該元素,並且得到元素的註釋與完整的key、value鍵值內容,也能修改改元素的value值。
config文件的結構

編程思想

我們的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;
    }

}

結束語

算法之所以有趣,是因爲算法能將人的智慧賦予給計算機。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章