Java中实现grep -C 5功能:匹配关键词前后5行
刚好之前折腾过Unix4J的类似需求,给你整理两个可行的实现思路和代码示例,帮你实现类似grep -C 5 "Stack trace:"的上下文打印功能:
实现grep上下文打印的Unix4J方案
Unix4J确实没有直接对应-C(上下文行)的现成选项,但我们可以通过组合它的命令或者自定义逻辑来达成目标,下面是两种实用的实现方式:
方法一:组合Unix4J命令实现(适合批量处理文件)
这个方案可以遍历指定目录下的文件,逐个处理并捕获输出,完全贴合你的需求:
import org.unix4j.Unix4j; import org.unix4j.line.Line; import java.io.File; import java.util.List; public class GrepContextExample { public static void main(String[] args) { // 配置参数:目标关键词和上下文行数 String targetKeyword = "Stack trace:"; int contextLineCount = 5; // 遍历指定目录下的日志文件(可替换为你需要的路径和文件过滤规则) File targetDir = new File("./logs"); File[] logFiles = targetDir.listFiles(file -> file.isFile() && file.getName().endsWith(".log")); if (logFiles != null) { for (File logFile : logFiles) { System.out.println("=== 正在处理文件: " + logFile.getName() + " ==="); // 第一步:找出所有包含关键词的行号(Unix4J中行号从1开始计数) List<Long> matchedLineNumbers = Unix4j.grep(targetKeyword, logFile) .toLineStream() .map(Line::getLineNumber) .toList(); // 第二步:针对每个匹配行,提取前后contextLineCount行的内容 for (long lineNum : matchedLineNumbers) { long startLine = Math.max(1, lineNum - contextLineCount); long endLine = lineNum + contextLineCount; // 用sed命令提取指定行范围的内容,也可以将结果存入变量而非直接打印 List<String> contextContent = Unix4j.sed(String.format("%d,%dp", startLine, endLine), logFile) .toStringList(); // 输出上下文内容 contextContent.forEach(System.out::println); System.out.println("--- 上下文分割线 ---"); } } } } }
代码说明
- 定位匹配行:通过
grep找到所有包含关键词的行,再提取每行的行号。 - 计算上下文范围:对每个匹配行号,计算出需要输出的起始行(防止小于1)和结束行,用
sed命令提取对应范围的内容。 - 批量处理:通过
listFiles过滤目标文件,实现遍历处理的需求。
方法二:自定义行处理器(避免重复输出重叠上下文)
如果你的日志中存在关键词密集的情况,第一种方法可能会重复输出重叠的上下文行,这时可以用自定义逻辑先收集所有需要输出的行号,再统一过滤输出:
import org.unix4j.Unix4j; import org.unix4j.line.Line; import java.io.File; import java.util.HashSet; import java.util.Set; public class CustomGrepContext { public static void main(String[] args) { String targetKeyword = "Stack trace:"; int contextLineCount = 5; File targetFile = new File("application.log"); // 先收集所有需要输出的行号,避免重复 Set<Long> linesToOutput = new HashSet<>(); Unix4j.grep(targetKeyword, targetFile) .toLineStream() .forEach(line -> { long matchedLineNum = line.getLineNumber(); // 将匹配行前后contextLineCount行都加入集合 for (long i = Math.max(1, matchedLineNum - contextLineCount); i <= matchedLineNum + contextLineCount; i++) { linesToOutput.add(i); } }); // 过滤文件内容,只输出目标行 Unix4j.cat(targetFile) .toLineStream() .filter(line -> linesToOutput.contains(line.getLineNumber())) .forEach(line -> System.out.println(line.getContent())); } }
这种方法先把所有需要输出的行号存入集合,再一次性过滤文件内容,避免了重复处理重叠的上下文区域,效率更高。
内容的提问来源于stack exchange,提问作者Camilo Riviere
相关产品推荐
相关产品推荐

