读取文件进行修改,然后修改的内容写回文件

本文介绍Java中使用Pattern、Matcher和RandomAccessFile进行文件内容的查找替换操作,包括正则表达式的高级应用及文件读写技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

读取文件进行修改,然后修改的内容写回文件需求用到了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); //最多输出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, 0);
        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, -1); //输出所欲字符段,包括最后的空字符串
        for(String str : strs){
            System.out.println(str + ":  " + str.length());
        }
    }

运行,输出

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

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=0; 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[1024];
            int readNum = 0;
            rf.seek(25);
            while((readNum=rf.read(b)) > 0){
                System.out.println(new String(b, 0, 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(50); //如果向文件最后追加内容 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 - 1 - 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(1).trim();
            char[] charArray = fieldSrc.toCharArray();
            if (charArray[0] >= 65 && charArray[0] <= 90) {
                charArray[0] += 32;
                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 > 0) {
                String targetStr = split[1].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[0] >= 65 && fieldToChar[0] <= 90) {
                            fieldToChar[0]+=32;
                            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 - 2 - 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();
            }
        }

    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值