讀取檔案進行修改,然後修改的内容寫回檔案需求用到了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();
}
}
}