一道文字處理題目的思考

一寧發表於2014-03-02
 在網上碰到有網友問了這麼一道題,題目是這樣的:
 
java 寫入txt檔案,想要修改txt檔案每一行的第一個數字,加1;
例如txt檔案是:
1     1     5
2     2     10
3     3     15
 
轉變成:
2     1     5
3     2     10
4     3     15
 
看到題目的第一反應時可能需要正規表示式,而在java中使用raplaceAll("正規表示式","替換後的表示式")基本上就可以搞定了。但是有一個問題:正則匹配很好寫,reg = "^\\d+";就可以匹配每行的第一個數字了,但是替換成什麼呢?需要對每個數字加1,這個怎麼處理?使用捕獲組可以獲取我們需要處理的資料,但是捕獲後,無法進一步處理資料了。此條路不通之後,很不情願的想起另外一種辦法:按行處理。
 
掃描需要處理的文字,每掃描一行,就對該行進行匹配,匹配到資料之後,對該行處理,然後將該行寫入到新的檔案,整個文字掃描完成之後,資料也就處理完了。這個辦法是不是很笨拙?對於目前也沒有更好的方式(更好的方式也許可以使用excel來處理,但是要求使用程式設計來完成),就開始程式碼實現了:
 
// code version 1.0
 
開始寫程式碼時,發現資料之間都是以多個空格或者tab來分割,方便期間,使用split函式來處理吧。
 
     // java 解析文字,將每行第一個數字加1
     public static void writeFile() {
           BufferedReader reader = null;
           BufferedWriter writer = null;
            try{
                File file = new File( "new.txt");
                 if(!file.exists()) {
                     file.createNewFile();
                }
                StringBuffer sb = new StringBuffer();
                reader = new BufferedReader( new FileReader("test.txt"));
                String line = null;
                 //按行讀取
                 while((line = reader.readLine()) != null) {
                     String[] arr = line.split( "[ \t]++");
                      if(arr. length < 3) {
                           sb.append(line).append( "\r\n");
                            continue;
                     }
                      //獲取第一個數字,並加1
                      int num = Integer. valueOf(arr[0]);
                     num ++;
                     sb.append(num).append( "\t").append(arr[1]).append( "\t").append(arr[2]).append( "\r\n");
                }
                
                 //寫入新的檔案
                writer = new BufferedWriter( new FileWriter(file));
                writer.write(sb.toString());
                
           } catch (IOException e) {
                e.printStackTrace();
           } finally {
                 if(reader != null) {
                      try {
                           reader.close();
                     } catch (IOException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                }
                 if(writer != null) {
                      try {
                           writer.close();
                     } catch (IOException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                }
           }
           
     }
 
程式碼寫起來還是很順利的,但是有個問題,在資料處理完成之後:
int num = Integer.valueOf(arr[0]);
num ++;
怎麼把新的內容寫入到當前行中,也就是寫入當前的文字中?處理資料的時候是通過reader來按行讀取,如果需要將資料寫入的話,需要writer,在讀取檔案的時候,直接使用writer寫入資料,也不是件很容易的事。為了方便處理,果斷建立一個新的檔案,寫入新的資料,處理完之後,刪掉舊的檔案就是!
 
程式碼實現之後,就跟一個朋友商討了下,朋友說,可以使用正則來完成,split的效率有點低。OK,把主要的處理過程重新實現了下:
// code version 1.1
            Pattern pattern = Pattern. compile("^\\d+" );
            while(...) {
               Matcher matcher = pattern .matcher(line);
               if(matcher.find()) {
                     String str = matcher.group();
                     int n = Integer.parseInt(str);
                     n ++;
                     line = line.replaceFirst(str, String.valueOf(n));
                     sb.append(line).append( "\r\n");
               }
            }     
 
除了將split修改為Pattern之後,同時,行資料也保持原來的風格保持了不變:
line = line.replaceFirst(str, String.valueOf(n));
 
但是為什麼Pattern會比split的效率高呢?
split的實現中,會呼叫Pattern.compile("...");也就是在對文字每行的處理中,如果使用split,則每次都會新建一個Pattern.compile("...")物件。而在使用Pattern類,只在最開始生成一個Pattern.compile("...")物件,減少了記憶體的開銷;
 
 
一個前輩說,不需要正則,使用indexOf和substring可以提高效率。同時,他建議不要使用BufferedWriter,應當使用printStream。
恩,開始修改程式碼:
 
// code version 1.2
 
      public static void writeFile() throws IOException {
                BufferedReader reader = null;
                PrintStream writer = null;
            
                File file = new File( "new.txt");
                 if(!file.exists()) {
                     file.createNewFile();
                }
                writer = new PrintStream( new FileOutputStream(file));
                reader = new BufferedReader( new FileReader("test.txt" ));
                String line = null;
                 //按行讀取
                 while((line = reader.readLine()) != null) {
                      //這裡通過index來確定需要處理的資料
                      int index = line.indexOf( " ");
                      if(index == -1) {
                            continue;
                      }
                      int num = Integer.parseInt(line.substring(0,index))+1;
                      line = num + line.substring(index);
                      writer.println(line);
                }
               // ....
           
     }
 
使用indexOf和substring 替換掉正則之後,邏輯似乎也清晰了許多,由於去掉了正規表示式的一些處理,直接對字串處理,效率上應該會有一些提高。但是使用PrintStream 替換掉BufferedWriter是不是就是個好主意?不見得,BufferedWriter作為一個具有緩衝功能的包裝類,效能上比其他類要高很多。而且在處理文字時,每處理一行,就向檔案中寫入資料,這個效能也不見得很高。權衡之際,提高處理資料效率,使用indexOf和substring,寫檔案時,採用BufferedWriter將資料寫入緩衝,提高效率。
 
// code version 1.3
 
      public static void writeFile() throws IOException {
                BufferedReader reader = null;
                BufferedWriter writer = null;
            
                writer = new BufferedWriter( new FileWriter("new.txt" ));
                reader = new BufferedReader( new FileReader("test.txt" ));
                String line = null;
                 //按行讀取
                 while((line = reader.readLine()) != null) {
                      //這裡通過index來確定需要處理的資料
                     int index = line.indexOf( " ");
                     if(index == -1) {
                            continue;
                     }
                     int num = Integer.parseInt(line.substring(0,index))+1;
                     line = num + line.substring(index);
                     writer.write(line);
                     writer.newLine();
                }
                
               //...
     }
 
到此,關於該題目的編碼總算塵埃落定。期間,經歷了幾番波折,從split->Pattern->indexOf再到流的選取,糾結了較長時間。總結一下:
 
1、儘量不要使用正規表示式,可以使用indexOf和substring來代替正則;必須使用正則的情況下,使用Pattern類能夠提高效率。
2、使用Buffer之類的包裝類,可以提高效率。
 
但是,還是有幾個問題留作以後慢慢考慮吧。
1、直接使用正則或者是否還有其他更簡單的處理方式麼?
2、如何直接寫入到當前的檔案中?
 

相關文章