读取文件进行修改,然后修改的内容写回文件需求用到了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();
}
}
}