先说下需求。在做自动化测试的时候,每个用例的配置文件是不一样的,于是需要在执行的时候先将用例上传,然后再执行,这边就有个问题。我的用例可能是半年前写的,所以当时的配置文件和现在最新的配置文件已经有了变化,主要是新配置文件会有新的配置项加进来。这种情况下,如果只是简单替换,那么有些关键配置的缺失会导致程序起不来。于是我便想到,每次上传的时候,都用最新的配置项,只是把用例里边的配置值拷贝过来就可以了。
比如最新的配置为new.conf,用例里的配置为old.conf。
new.conf:
#instand id
id=sitea
# MPCC customer default time zone
default_time_zone=GMT-05:00
# data persistent cron job default configuration
data_persistent_cron=0/15 * * * * ?
# data synchronization cron job configuration
data_synchronization_cron=25 0 0 * * ?
old.conf:
#instand id
id=site_def
# MPCC customer default time zone
default_time_zone=GMT-08:00
# data persistent cron job default configuration
data_persistent_cron=0/5 * * * * ?
开始我想到了用Java的properties来操作,分别读取比较,value不一样就更新。可是我发现更新之后的数据是这个样子的:
id=site_def
default_time_zone=GMT-08:00
data_persistent_cron=0/5 * * * * ?
data_synchronization_cron=25 0 0 * * ?
这显然不是我想要的结果。网上查了下没有看到properties如果解决这个问题(有知道的朋友麻烦说一下),于是便想用RandomAccessFile。
关于RandomAccessFile,网上有各种介绍,我这里只说我用到的。
- 新建RandomAccessFile对象
RandomAccessFile raf = new RandomAccessFile(new File(filepath), "rw");
- pointer概念
pointer就是指针。RandomAccessFile支持文件的随机读写,随机的方法就是通过pointer实现,它指向了当前文件的读写位置,以byte进行计数。
- pointer的定位
文件初始化时,pointer的值为0,即为文件的最开始,当我们读取了一行数据,如果有这行数据有8个byte,那么读取完毕后,pointer的值就是8,如果有换行的话,那么换行字符也要加上,如果是\r\n,就再加两个字符,pointer的值就是10。可以通过getFilePointer()来会去当前的pointer位置。写文件也会改变pointer的位置。
如果要重新定位pointer,可以通过seek()方法,比如到到文件开头,那就seek(0)
- 文件的长度可以用length()方法获取。
- 可以将文件写入到一个buffer中,也可以将文件从buffer中读取。命令分别为:
raf.read(buffer, 0, (int)(raf.length() - point + 1)); 注意此处第二个参数指的是buffer的索引
raf.write(buffer);
RandomAccessFile的buffer读写都是和pointer有关的,读取的是pointer开始后的length个字节,写也是写到pointer之后
下面是代码:
public static void compareConfig(File oldConf, File newConf)
try {
Properties pps = new Properties();
pps.load(new FileInputStream(oldConf)); //将case中的配置读取到properties对象中
RandomAccessFile raf = new RandomAccessFile(newConf, "rw"); //将新配置读取到RandomAccessFile对象中,此处访问方式是读写
long lastPoint = 0; //定义RandomAccessFile 对象的pointer
String line = "";
while((line = raf.readLine()) != null) {
long point = raf.getFilePointer(); //每读一行,都记录下读取后的pointer位置
System.out.println(point);
if(!line.trim().equals("") && !line.trim().startsWith("#") && line.trim().indexOf("=") != -1) //判断是不是注释或者空行
{
String[] item = line.split("="); //去配置的key和value
String key = item[0];
String oldValue = item[1];
String newValue = "";
if(pps.containsKey(key)) { //如果在case的配置中存在,就取出来case中的配置值
newValue = pps.getProperty(key);
} else {
lastPoint = point; //如果case中的配置不存在,那就读取新配置的下一行,读取前,保存当前的pointer位置
continue;
}
if(oldValue.trim().equals(newValue.trim())) { //值相等,就continue
lastPoint = point;
continue;
}
//下边是配置相同但是值不同的逻辑
String newLine = line.replace(oldValue, newValue);//先用新值换旧值
int readBytes = (int) (raf.length() - point + 1);
byte[] buffer = new byte[readBytes]; //创建一个buffer,大小是newConf文件剩余的bytes数目
if(oldValue.length() > newValue.length()) { //处理替换的新值比旧值短的情况,比如例子中的id,缺的用空格补齐
int n = oldValue.length() - newValue.length();
while(n>0) {
newLine = newLine + " ";
n--;
}
} else if(oldValue.length() < newValue.length()) { //处理新值比旧值长的情况,将剩余的字节读取到buffer中
raf.seek(point-1); //将回车也拷贝到buffer中
raf.read(buffer, 0, (int)(raf.length() - point + 1));
}
raf.seek(lastPoint); //找到上次读取的位置
raf.writeBytes(newLine); //写入新值
if(oldValue.length() < newValue.length()) { //对于新值比旧值长,将buffer回写
lastPoint = point + newValue.length() - oldValue.length();
raf.write(buffer);
} else {
lastPoint = point; //新值比旧值短,简单记录上次写的位置,因为前面空格补齐了,所以pointer值不变
}
raf.seek(point);
} else {
lastPoint = point;
}
raf.seek(lastPoint); //最后将pointer定位在上次读或者写的地方
}
raf.close();
} catch (IOException e) {
// TODO Auto-generated catch block
}
}
这个程序其实可以更精简一些,这里就不做了