Java Scanner to print previous and next lines

2019-02-27 08:40发布

I am using 'java.util.Scanner' to read and scan for keywords and want to print the previous 5 lines and next 5 lines of the encountered keyword, below is my code

 ArrayList<String> keywords = new ArrayList<String>();      
 keywords.add("ERROR");             
 keywords.add("EXCEPTION");
 java.io.File file = new java.io.File(LOG_FILE);
      Scanner input = null;
    try {
        input = new Scanner(file);
    } catch (FileNotFoundException e) {         
        e.printStackTrace();
    }
      int count = 0;
      String previousLine = null;
      while(input.hasNext()){
          String line = input.nextLine();
          for(String keyword : keywords){                
              if(line.contains(keyword)){                                             
                  //print prev 5 lines
                  system.out.println(previousLine); // this will print only last previous line ( i need last 5 previous lines)
                  ???
                  //print next 5 lines                    
                  system.out.println(input.nextLine());
                  system.out.println(input.nextLine());
                  system.out.println(input.nextLine());
                  system.out.println(input.nextLine());
                  system.out.println(input.nextLine());
              }
              previousLine = line;
          }

any pointers to print previous 5 lines..?

2条回答
爷的心禁止访问
2楼-- · 2019-02-27 09:12

any pointers to print previous 5 lines..?

  • Save them in an Dequeue<String> such as a LinkedList<String> for its "First In First Out (FIFO)" behavior.
  • Either that or use 5 variables or an array of 5 Strings, manually move Strings from one slot or variable to another, and then print them.
  • If you use Dequeue/LinkedList, use the Dequeue's addFirst(...) method to add a new String to the beginning and removeLast() to remove the list's last String (if its size is > 5). Iterate through the LinkedList to get the current Strings it contains.

Other suggestions:

  • Your Scanner's check scanner.hasNextXXX() method should match the get method, scanner.nextXXX(). So you should check for hasNextLine() if you're going to call nextLine(). Otherwise you risk problems.
  • Please try to post real code here in your questions, not sort-of, will never compile code. i.e., system.out.println vs System.out.println. I know it's a little thing, but it means a lot when others try to play with your code.
  • Use ArrayList's contains(...) method to get rid of that for loop.

e.g.,

  LinkedList<String> fivePrevLines = new LinkedList<>();
  java.io.File file = new java.io.File(LOG_FILE);
  Scanner input = null;
  try {
     input = new Scanner(file);
  } catch (FileNotFoundException e) {
     e.printStackTrace();
  }
  while (input.hasNextLine()) {
     String line = input.nextLine();
     if (keywords.contains(line)) {
        System.out.println("keyword found!");
        for (String prevLine : fivePrevLines) {
           System.out.println(prevLine);
        }
     } else {
        fivePrevLines.addFirst(line);
        if (fivePrevLines.size() > 5) {
           fivePrevLines.removeLast();
        }
     }
  }
  if (input != null) {
     input.close();
  }

Edit
You state in comment:

ok i ran small test program to see if the contains(...) method works ...<unreadable unformatted code>... and this returned keyword not found...!

It's all how you use it. The contains(...) method works to check if a Collection contains another object. It won't work if you feed it a huge String that may or may not use one of the Strings in the collection, but will work on the individual Strings that comprise the larger String. For example:

  ArrayList<String> temp = new ArrayList<String>();
  temp.add("error");
  temp.add("exception");
  String s = "Internal Exception: org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object";
  String[] tokens = s.split("[\\s\\.:,]+");
  for (String token : tokens) {
     if (temp.contains(token.toLowerCase())) {
        System.out.println("keyword found:     " + token);
     } else {
        System.out.println("keyword not found: " + token);
     }
  }

Also, you will want to avoid posting code in comments since they don't retain their formatting and are unreadable and untestable. Instead edit your original question and post a comment to alert us to the edit.


Edit 2
As per dspyz:

For stacks and queues, when there isn't any significant functionality/performance reason to use one over the other, you should default to ArrayDeque rather than LinkedList. It's generally faster, takes up less memory, and requires less garbage collection.

查看更多
放荡不羁爱自由
3楼-- · 2019-02-27 09:15

If your file is small (< a million lines) you are way better off just copying the lines into an ArrayList and then getting the next and previous 5 lines using random access into the array.

Sometimes the best solution is just plain brute force.

Your code is going to get tricky if you have two keyword hits inside your +-5 line window. Let's say you have hits two lines apart. Do you dump two 10-line windows? One 12-line window?

Random access will make implementing this stuff way easier.

查看更多
登录 后发表回答