如何提升Java Scanner日志分析程序的健壮性?
Hey there! Let's tackle your Java log processing tool—first off, great job getting a working version up even if you don't use Java day-to-day. I totally get the frustration with java.util.Scanner's wonky behavior, and we can fix that while also making your program handle raw logs without needing preprocessing. Here's how to speed up your optimization:
1. Ditch Scanner's Line-by-Line Hassle with Matcher
The biggest issue right now is that Scanner.findInLine() can get confused by non-target lines. Instead, read each line explicitly and use Matcher to check for your patterns—this way, we skip non-matching lines automatically, no preprocessing needed.
Replace your run() method with this cleaner approach:
public void run(String[] args) throws Exception { try (BufferedReader br = new BufferedReader(new FileReader(m_file))) { String line; while ((line = br.readLine()) != null) { Matcher startMatcher = m_startRunPattern.matcher(line); Matcher endMatcher = m_currentTimePattern.matcher(line); if (startMatcher.find() && endMatcher.find()) { String beginTimeStr = startMatcher.group(1); String endTimeStr = endMatcher.group(1); calculateAndPrintDuration(beginTimeStr, endTimeStr); } // Non-matching lines are just skipped—no extra work needed! } } catch (IOException exception) { System.err.println("Error reading file: " + exception.getMessage()); } }
I swapped Scanner for BufferedReader here too—it's faster for large log files, which is a nice bonus.
2. Simplify Date Math with Java 8+ Time API
Your current SimpleDateFormat is thread-unsafe and a bit clunky. Java 8's java.time package is way better for date operations, and it handles your microsecond timestamps seamlessly.
First, update your date formatter:
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH.mm.ss.SSSSSS");
Then add a helper method for duration calculation (cleaner than jamming it all in the loop):
private void calculateAndPrintDuration(String startStr, String endStr) { try { LocalDateTime start = LocalDateTime.parse(startStr, DATE_FORMATTER); LocalDateTime end = LocalDateTime.parse(endStr, DATE_FORMATTER); Duration duration = Duration.between(start, end); long totalSeconds = duration.getSeconds(); long minutes = totalSeconds / 60; long remainingSeconds = totalSeconds % 60; System.out.printf("Elapsed seconds: %d, (%d minutes, %d seconds)%n", totalSeconds, minutes, remainingSeconds); } catch (DateTimeParseException e) { System.err.printf("Warning: Could not parse timestamps '%s' / '%s' - %s%n", startStr, endStr, e.getMessage()); } }
Notice I fixed the remainder calculation here—your original code had a bug where diffInSeconds % diffInMinutes would give wrong results (e.g., 100 seconds would show 0 remaining seconds instead of 40).
3. Quick Wins for Usability & Robustness
- Friendly Error Messages: Replace your "nope." with a proper usage prompt when no file is provided:
if (args.length == 0) { System.err.println("Usage: java TimeDiff <path-to-log-file>"); System.exit(1); } - Static Pattern Best Practice: You already precompiled your patterns as
staticvariables—great call! This avoids redundant regex compilation and speeds up processing.
Full Updated Code
Here's the complete revised program with all these changes:
import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.time.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.regex.Matcher; import java.util.regex.Pattern; public class TimeDiff { private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH.mm.ss.SSSSSS"); private static final Pattern m_startRunPattern = Pattern.compile("start of run=([^,]+)"); private static final Pattern m_currentTimePattern = Pattern.compile("current time=(.+)"); private final File m_file; public TimeDiff(String[] args) { if (args.length == 0) { System.err.println("Usage: java TimeDiff <path-to-log-file>"); System.exit(1); } m_file = new File(args[0]); } private void calculateAndPrintDuration(String startStr, String endStr) { try { LocalDateTime start = LocalDateTime.parse(startStr, DATE_FORMATTER); LocalDateTime end = LocalDateTime.parse(endStr, DATE_FORMATTER); Duration duration = Duration.between(start, end); long totalSeconds = duration.getSeconds(); long minutes = totalSeconds / 60; long remainingSeconds = totalSeconds % 60; System.out.printf("Elapsed seconds: %d, (%d minutes, %d seconds)%n", totalSeconds, minutes, remainingSeconds); } catch (DateTimeParseException e) { System.err.printf("Warning: Could not parse timestamps '%s' / '%s' - %s%n", startStr, endStr, e.getMessage()); } } public void run() throws Exception { try (BufferedReader br = new BufferedReader(new FileReader(m_file))) { String line; while ((line = br.readLine()) != null) { Matcher startMatcher = m_startRunPattern.matcher(line); Matcher endMatcher = m_currentTimePattern.matcher(line); if (startMatcher.find() && endMatcher.find()) { calculateAndPrintDuration(startMatcher.group(1), endMatcher.group(1)); } } } catch (IOException exception) { System.err.println("Error reading log file: " + exception.getMessage()); } } public static void main(String args[]) { try { TimeDiff app = new TimeDiff(args); app.run(); } catch (Exception e) { e.printStackTrace(); } } }
This version will handle your raw logs directly (no need for vi or sed preprocessing), has clearer error handling, and uses modern Java features that are more reliable. You can compile and run it just like your original, but now it works with unmodified logs!
内容的提问来源于stack exchange,提问作者Kelly Beard

