昨天遇到一个问题,某台 Jenkins 服务从 Mac 移植到 Windows,不得不把之前的 shell 脚本用 bat 实现,回想以前也遇到过这种情况
文件操作是比较常用的场景,以前用对于的增、删、改、查的实现比较多,在此基础上可以再丰富些功能,那么想法转化成行动吧
需求点
- 用代码实现 grep,awk,sed 等的部分功能,做成插件,可用于日志的过滤、查询、文件数据的查询、统计等,以替代依赖于平台的脚本,提高移植性;
- 支持对文件的操作,也支持对已存在于内存的字符串进行操作,分为过滤和查询统计两大功能;
- 作为一个插件,可扩展性要高。
设计和解决思路
- 设计过滤、查询统计两个接口,使用泛型(规范和扩展);
- 文件操作和字符串操作实现这两个接口;
部分实现类代码
/**
* 设置筛选内容,过滤出包含筛选内容的行
* @param fname 文件路径
* @param filter
* @return 包含筛选内容的所有行
*/
public String grep(String fname, String filter) {
String find = "";
String line;
try {
inputStream = new FileInputStream(fname);
encode = EncodingDetect.getJavaEncode(fname);
sc = new Scanner(inputStream, encode);
while (sc.hasNextLine()) {
line = sc.nextLine();
if (line.contains(filter))
find = find + line + System.getProperty("line.separator");
}
} catch (FileNotFoundException e) {
find = "";
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (sc != null) {
sc.close();
}
} catch (IOException e) {
find = "";
e.printStackTrace();
}
}
return find.trim();
}
/**
* 从文件末尾开始,按指定行数输出
* @param fileName 文件路径
* @param lines 行数
* @return 符合条件的行
*/
public String tail(String fileName, int lines) {
boolean findFirstNonBlankLine = false;
String line;
String content = "";
RandomAccessFile rf = null;
try {
rf = new RandomAccessFile(fileName, "r");
long len = rf.length();
long start = rf.getFilePointer();
long nextend = start + len - 1;
rf.seek(nextend);
int c = -1;
while (nextend > start) {
c = rf.read();
if (c == '\n' || c == '\r') {
line = rf.readLine();
if (lines > 0) {
if (null == line || line.trim().isEmpty()) {
if (findFirstNonBlankLine) {
content = line + System.getProperty("line.separator") + content;
lines--;
} else {
continue;
}
} else {
content = line + System.getProperty("line.separator") + content;
findFirstNonBlankLine = true;
lines--;
}
}
nextend--;
}
nextend--;
if (lines == 0)
break;
rf.seek(nextend);
// 当文件指针退至文件开始处,输出第一行
if (nextend == 0) {
line = rf.readLine();
content = line + System.getProperty("line.separator") + content;
break;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (rf != null) {
rf.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return content.trim();
}
/**
* 文件查询,按指定列指定条件查询
* @param from 文件路径
* @param where 查询条件的列标
* @param condition 查询条件
* @param column 查询的字段
* @return 符合条件的查询结果
*/
public List<List<String>> select(String from, int where, String condition, int... column) {
String line;
String[] columnOfLine;
List<String> unitList = null;
List<List<String>> selectList = new ArrayList<List<String>>();
try {
inputStream = new FileInputStream(from);
encode = EncodingDetect.getJavaEncode(from);
sc = new Scanner(inputStream, encode);
while (sc.hasNextLine()) {
line = sc.nextLine();
line = line.trim().replaceAll(" +", " ").replaceAll("\t+", " ");
if (line.isEmpty()) {
continue;
}
unitList = new ArrayList<String>();
columnOfLine = line.split(" ");
if (columnOfLine[where - 1].trim().equals(condition)) {
if (null != column && column.length > 0) {
for (int i = 0; i < column.length; i++) {
unitList.add(columnOfLine[column[i] - 1].trim());
}
} else {
for (int i = 0; i < columnOfLine.length; i++) {
unitList.add(columnOfLine[i].trim());
}
}
selectList.add(unitList);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (sc != null) {
sc.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return selectList;
}
作为一个插件,虽然还不够完善,但至少解决了我目前的需求。日常工作中,总会遇到这样那样的问题,如能总结经验教训,转化成有用的东西,何乐而不为,希望对大家有帮助。