天天看點

讀取檔案進行修改,然後修改的内容寫回檔案

讀取檔案進行修改,然後修改的内容寫回檔案需求用到了Pattern、Matcher、RandomAccessFile。前三部分分别介紹三個類的用法,第四部分介紹“讀取檔案進行修改,然後修改的内容寫回檔案”需求的實作。

java.util.regex包下的Pattern和Matcher,再加上正規表達式,在java中可以實作非常強大的字元串處理功能。Pattern用來建立一個正規表達式的比對模式,Matcher用來根據正則比對字元串。下面舉例一些重要API的用法。

一、Pattern

1、public static boolean matches(String regex, CharSequence input)

matches方法用正規表達式reges去比對input輸入的字元串,如果比對成功傳回ture,比對失敗傳回false,注意是全比對。

private static String s1 = "abcdefg";
    @Test
    public void matchesTest(){
        boolean matches1 = Pattern.matches("\\w+", s1);
        /*部分比對會失敗*/
        boolean matches2 = Pattern.matches("abc", s1); 
        System.out.println("matches1 : " + matches1);
        System.out.println("matches2 : " + matches2);
    }
           

運作,輸出

matches1 : true
matches2 : false
           

2、public String[] split(CharSequence input)

split會根據指定的正規表達式分割輸入的字元串input,String中的split方法就是根據pattern中的split實作的。

private static String s2 = "aa:bb:cc";
    @Test
    public void splitTest(){
        /*根據雙引号分割s2字元串*/
        Pattern pattern = Pattern.compile(":");
        String[] strs = pattern.split(s2);
        for(String str : strs){
            System.out.println(str);
        }
    }
           

運作,輸出結果:

aa
bb
cc
           

3、public String[] split(CharSequence input, int limit)

改方法與上面的方法功能相似,多了一個limit參數。

當limit < 0時,會比對所有的字元串長度;

當limit > 0時,為n時,最多隻能比對n-1次;

當limit=0時,與上面方法相同,會比對所有字元串長度,并删除掉最後的空字元組。

當limit=3時

private static String s2 = "aa:bb:cc:ee";
    @Test
    public void splitTest(){
        Pattern pattern = Pattern.compile(":");
        String[] strs = pattern.split(s2, ); //最多輸出3組字元
        for(String str : strs){
            System.out.println(str + ":  " + str.length());
        }
    }
           

運作輸出:

aa:  2
bb:  2
cc:ee:  5
           

當limit=0時,”aa:bb:cc:ee:”被劃分成了”aa”、”bb”、”cc”、”ee”

private static String s2 = "aa:bb:cc:ee:";
    @Test
    public void splitTest(){
        Pattern pattern = Pattern.compile(":");
        String[] strs = pattern.split(s2, );
        for(String str : strs){
            System.out.println(str + ":" + str.length());
        }
    }
           

運作輸出

aa:2
bb:2
cc:2
ee:2
           

當limit<0時,”aa:bb:cc:ee:”被劃分成了”aa”、”bb”、”cc”、”ee”、”“

private static String s2 = "aa:bb:cc:ee:";
    @Test
    public void splitTest(){
        Pattern pattern = Pattern.compile(":");
        String[] strs = pattern.split(s2, -); //輸出所欲字元段,包括最後的空字元串
        for(String str : strs){
            System.out.println(str + ":  " + str.length());
        }
    }
           

運作,輸出

aa:  
bb:  
cc:  
ee:  
:  
           

4、public Matcher matcher(CharSequence input)

該方法用于生成Matcher類,該類用于比對字元串

public void matcherTest(){
        Pattern pattern = Pattern.compile("\\w+");
        Matcher matcher = pattern.matcher(s1);
    }
           

二、Matcher類

Pattern類主要用來建立正則比對模式,也可以判斷比對是否成功,但是如果想用比對更強大的功能,比如比對的内容,比對的次數等資訊,Pattern要與Matcher連用更強大。

1、public boolean matches()

matchers()對整個字元串比對,比對成功,傳回true,比對失敗,傳回false

private static String s3 = "abcdefg1234";
    @Test
    public void matcherTest(){
        Pattern pattern1 = Pattern.compile("\\w+"); //比對所有字元和數字,包括下劃線
        Pattern pattern2 = Pattern.compile("[a-z]+"); //比對所有小寫字元
        Matcher matcher1 = pattern1.matcher(s3);
        Matcher matcher2 = pattern2.matcher(s3);
        boolean b1 = matcher1.matches();
        boolean b2 = matcher2.matches();
        System.out.println(b1);  //輸出true
        System.out.println(b2);  //輸出false
    }
           

2、public boolean lookingAt()

隻對字元串的開始進行比對,如果比對成功,傳回true,比對失敗傳回false

private static String s4 = "abcdefg12345";
    @Test
    public void lookingAtTest(){
        Pattern pattern1 = Pattern.compile("[a-z]+"); //比對開始是字元
        Pattern pattern2 = Pattern.compile("[0-9]+"); //比對開始是數字
        Matcher matcher1 = pattern1.matcher(s4);
        Matcher matcher2 = pattern2.matcher(s4);
        System.out.println(matcher1.lookingAt()); //輸出true
        System.out.println(matcher2.lookingAt()); //輸出false
    }
           

3、public boolean find()

不管字元串的什麼位置,隻要符合比對模式,傳回true,不比對,傳回失敗

private static String s5 = "abc123cdef";
    @Test
    public void findTest(){
        Pattern pattern = Pattern.compile("[0-9]+");
        Matcher matcher = pattern.matcher(s5);
        System.out.println(matcher.find()); //傳回true
    }
           

4、public int start() / public int end()

start()傳回比對到字元串的開始位置,end()傳回比對到字元串的結束位置

private static String s6 = "abcde12345fghij";
    @Test
    public void startAndEndTest(){
        Pattern pattern = Pattern.compile("[a-z]+");
        Matcher matcher = pattern.matcher(s6);
        while(matcher.find()){
            System.out.println("比對的字元串:" + matcher.group() + ";比對字元串起始位置:" + matcher.start()
            + ";比對字元串結束位置:" + matcher.end());
            System.out.println("-----------------------");
        }
    }
           

運作,輸出

比對的字元串:abcde;比對字元串起始位置:0;比對字元串結束位置:5
-----------------------
比對的字元串:fghij;比對字元串起始位置:10;比對字元串結束位置:15
-----------------------
           

5、public String group()

group()傳回比對到字元串,見上一示例。

6、public int groupCount()

傳回比對到的組數

private static String s7 = "12345-34-5656";
    @Test
    public void groupCountTest(){
        Pattern pattern = Pattern.compile("(\\d+)-(\\d+)-(\\d+)");
        Matcher matcher = pattern.matcher(s7);
        if(matcher.find()){
            int groupCount = matcher.groupCount(); //比對模式中有三個小括号,是以groupCount=3,比對到3組。即group(1)、group(2)、group(3)。注意,group(0)表示比對到的整個字元串
            for(int i=; i<=groupCount; i++){
                System.out.println(matcher.group(i));
            }
        }
    }
           

運作輸出:

12345-34-5656
12345
34
5656
           

在上标題中,group()底層就是用group(0)實作的,表示比對到的整個字元串。

注意:一定要在用grou()、groupCount()、group(i)擷取比對字元串前,用matches()/looingAt()/find()進行比對,否則後面不能擷取字元串,會報錯。

7、public Matcher reset()

表示清除比對,重新進行比對。

8、public Matcher appendReplacement(StringBuffer sb, String replacement) 和

public StringBuffer appendTail(StringBuffer sb)

appendReplacement用于把比對到的字元串前面的内容複制到sb中,然後把比對的内容替換成replacement,追加到sb中;appendTail用于把比對到的字元串後面的内容追加到sb中

private static String s8 = "123sdfs456dffs789";
    @Test
    public void appendTest(){
        Pattern pattern = Pattern.compile("[a-z]+");
        Matcher matcher = pattern.matcher(s8);
        StringBuffer sb = new StringBuffer();
        while(matcher.find()){
            matcher.appendReplacement(sb, "lzj");
        }
        System.out.println(sb); //輸出:123lzj456lzj
        matcher.appendTail(sb);
        System.out.println(sb); //輸出:123lzj456lzj789
    }
           

三、RandomAccessFile

RandomAccessFile是java Io體系中功能最豐富的檔案内容通路類。即可以讀取檔案内容,也可以向檔案中寫入内容。但是和其他輸入/輸入流不同的是,程式可以直接跳到檔案的任意位置來讀寫資料。RandomAccessFile比IO流中的reader、write、InputStream、OutputStream功能更強大的之處可以同時建立讀寫雙向管道,可以定位從檔案中的任何位置進行讀寫。

1、****public RandomAccessFile(String name, String mode)或

public RandomAccessFile(File file, String mode)

兩個構造函數建立檔案句柄,mode表示讀寫模式:

  • “r” 以隻讀方式來打開指定檔案夾。如果試圖對該RandomAccessFile執行寫入方法,都将抛出IOException異常。
  • “rw” 以讀,寫方式打開指定檔案。如果該檔案尚不存在,則試圖建立該檔案。
  • “rws” 以讀,寫方式打開指定檔案。相對于”rw” 模式,還要求對檔案内容或中繼資料的每個更新都同步寫入到底層裝置。
  • “rwd” 以讀,寫方式打開指定檔案。相對于”rw” 模式,還要求對檔案内容每個更新都同步寫入到底層裝置。

2、public native long getFilePointer()擷取檔案指針位置

3、public void seek(long pos)設定檔案指針位置

4、在任意指定位置開始讀檔案

public void testRandomAccessFileRreader(){
        RandomAccessFile rf = null;
        try {
            rf = new RandomAccessFile("e:/123.txt", "r");
            byte[] b = new byte[];
            int readNum = ;
            rf.seek();
            while((readNum=rf.read(b)) > ){
                System.out.println(new String(b, , readNum));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                rf.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
           

5、在任意指定位置寫檔案

@Test
    public void testRandomAccessFileWrite(){
        RandomAccessFile rf = null;
        try {
            rf = new RandomAccessFile("e:/123.txt", "rw");
            rf.seek(); //如果向檔案最後追加内容 rf.seek(rf.length())
            rf.writeBytes("hello RandomAccessFile");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                rf.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
           

四、示例:有一個需求,需要對Mybatis中的mapper檔案中變量名首字母為大寫的轉化為小寫字母。可以用RandomAccessFile讀取和寫入檔案,用Pattern和Matcher進行比對字元。

源檔案為

<if test="Email != NULL and '' = Tmain">
        email=#{Email}, name=#{Name}
</if>

<if test="Email != NULL and '' = Tmain">
        email=#{Email}, name=#{Name}
</if>
           

現需要把檔案中的test中的變量Email,以及#{Email}、#{Name}中的變量的首字母大寫轉化為小寫,資料庫字段名不需要轉大小寫。

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test1 {

    public static void main(String[] args) throws IOException {
        exec1();
    }

    public static void exec1(){
        RandomAccessFile rf = null;
        try {
            rf = new RandomAccessFile("e:/test.xml", "rw");
            String readLine = rf.readLine();
            while (readLine != null) {
                long filePointer = rf.getFilePointer();
                String writeLineIf = patternProcessIf(readLine);
                String writeLineField = patternProcessField(writeLineIf);
                if (!readLine.equals(writeLineField)) {
                    /*xml檔案的換行符是一個'\n',一個位元組*/
                    rf.seek(filePointer -  - readLine.length());
                    rf.writeBytes(writeLineField + "\n");
                }
                readLine = rf.readLine();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                rf.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /*
     * email=#{email}, name=#{Name}
     * */
    public static String patternProcessField(String str){
        System.out.println("str : " + str);
        String strDst = str;
        Pattern pattern = Pattern.compile("#\\{(\\s*\\w+\\s*)\\}");
        Matcher matcher = pattern.matcher(str);
        String fieldSrc = null;
        String fieldDst = null;
        while(matcher.find()){
            fieldSrc = matcher.group().trim();
            char[] charArray = fieldSrc.toCharArray();
            if (charArray[] >=  && charArray[] <= ) {
                charArray[] += ;
                fieldDst = String.valueOf(charArray);
                strDst = strDst.replace(fieldSrc, fieldDst);
            }
        }
        System.out.println("strDst : " + strDst);
        return strDst;
    }

    /*
     *      <if test="email != null and '' != emain">
                email=#{email}, name=#{name}
            </if>
     * */
    public static String patternProcessIf(String str){
        String strDst = str;
        String regexIf = "<\\s*if\\s+test\\s*=";
        Pattern patternIf = Pattern.compile(regexIf);
        Matcher matcherIf = patternIf.matcher(str);
        if (matcherIf.find()) {
            Pattern patternSplit = Pattern.compile("\"");
            String[] split = patternSplit.split(str);
            if (split.length > ) {
                String targetStr = split[].trim();
                String regexTargetStr = "and|=|!=";
                String[] targetStrSplit = targetStr.split(regexTargetStr);
                String fieldSrc = null;
                String fieldDst = null;
                for(String fieldStr : targetStrSplit){
                    fieldSrc = fieldStr.trim();
                    char[] fieldToChar = null;
                    if (!fieldSrc.equalsIgnoreCase("null") && !fieldSrc.equalsIgnoreCase("''")) {
                        fieldToChar = fieldSrc.toCharArray();
                        if (fieldToChar[] >=  && fieldToChar[] <= ) {
                            fieldToChar[]+=;
                            fieldDst = String.valueOf(fieldToChar);
                            strDst = strDst.replaceAll(fieldSrc, fieldDst);
                        }
                    }
                }

            }
        }
        return strDst;
    }
}
           

運作程式,結果,轉化的檔案為:

<if test="email != NULL and '' = emain">
        email=#{email}, name=#{name}
</if>
<if test="email != NULL and '' = emain">
        email=#{email}, name=#{name}
</if>
           

執行成功。

注意,本程式處理的是xml檔案,換行符是一個’\n’,如果要處理window上的一個txt檔案,把test.xml改成test.txt,那麼換行符是兩個位元組的’\r\n’,是以exec1方法要修改為如下,其它方法不變。

public static void exec1(){
        RandomAccessFile rf = null;
        try {
            rf = new RandomAccessFile("e:/test.txt", "rw");
            String readLine = rf.readLine();
            while (readLine != null) {
                long filePointer = rf.getFilePointer();
                String writeLineIf = patternProcessIf(readLine);
                String writeLineField = patternProcessField(writeLineIf);
                if (!readLine.equals(writeLineField)) {
                    rf.seek(filePointer -  - readLine.length());
                    rf.writeBytes(writeLineField + "\r\n");
                }
                readLine = rf.readLine();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                rf.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }