-1

I'm attempting to read two lines in a log, compare the times, and then get the difference between the two. So for example input file...

20 Mar 2012 19:10:33 
20 Mar 2012 19:10:34

After I get the difference of them, I'm going to Thread.Sleep(difference) in my program. My problem is this: I'm not sure how to read two different lines of a file at the same time.

Any and all help is much appreciated. Thanks for your time!

Bob
  • 1,344
  • 3
  • 29
  • 63
  • 1
    How are you reading one line of the file ? Are you using Buffered Reader ? – Kakarot Sep 08 '16 at 21:06
  • 4
    why at same time? its a log, it will remain there, you can read first one, then second one – Mayday Sep 08 '16 at 21:07
  • The delay between reading one line of a file and the next one is miniscule. – ifly6 Sep 08 '16 at 21:09
  • @Kakarot I'm using a buffered reader. – Bob Sep 08 '16 at 21:35
  • @Mayday Because after each line ready, I want to send that line to a program with the correct time difference between the two – Bob Sep 08 '16 at 21:36
  • Your Question does not make sense. If you have a file with two lines, read until you reach end of file. What exactly is your issue? – Basil Bourque Sep 09 '16 at 06:26
  • @BasilBourque it has around 40,000 lines that I need to output in the relative times they were received based on the field I showed in my small example of input data – Bob Sep 09 '16 at 13:02
  • Also, it's unclear why you want to sleep at all, especially if you've got 40,000 lines. You generally only want to sleep if you have to wait for something, and this doesn't appear to be the case. – Clockwork-Muse Sep 09 '16 at 22:22

1 Answers1

0

Read lines successively

You seem to be stuck on this idea of reading pairs of lines together. No need for this simultaneous reading. Read one line, then read the next. You now have a pair of inputs to process. Then proceed to read the next line and the one after that. Process again. Lather, rinse, repeat.

Perhaps you are concerned about the performance implications of reading from storage. The BufferedReader implementation handles the challenge of efficiently reading from storage, buffering chunks of data from the file in cache memory. So your pairs of lines are likely together in memory already. You can tweak the amount of buffer but the default is usually fine. A BufferedReader can usually read millions of lines per second, so not likely to be real problem. Beware of premature optimization.

If you are really concerned about performance, use java.nio.file.Files.readAllLines to read all the lines into memory at once. But, obviously, you must have enough free memory for the size of your input file. So the BufferedReader is safer in case of unexpectedly large files.

Example code

In this example code we label each pair of lines read as odd-numbered and even-numbered given that we expect pairs.

Find your file in storage. Mine is named log.txt in my home folder.

// Locate file in storage.
String homeFolder = System.getProperty ( "user.home" );
String fileName = "log.txt";
Path p = FileSystems.getDefault ().getPath ( homeFolder , fileName );

Define a DateTimeFormatter to match the expected format of our input data. Note that we are using the modern java.time classes. Avoid the old legacy date-time classes that have proven to be so terribly confusing and troublesome.

This format used in your data file is terrible: Assumes English, Abuses English with incorrect abbreviations, and is tricky to parse with SPACE characters. Instead of inventing such patterns, I strongly recommend using standard ISO 8601 formats when generating strings that represent date-time values.

// Define parser for expected format of data in file.
// Bad choice of format. Should have written data in standard ISO 8601 format instead.
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd MMM uuuu HH:mm:ss" , Locale.US );

Read pairs of lines successively. Parse each pair of inputs. Since the input data unfortunately lacks any indication of offset-from-UTC or time zone, we parse as LocalDateTime.

The elapsed time between them will be calculated in a Duration using generic 24-hour days while ignoring anomalies such as Daylight Saving Time (DST). Better to be using values with explicit offset or time zone the omission leaves us wondering about the true meaning of these date-time inputs.

// Read through the lines in the file, reading pairs of odd-numbered and even-numbered lines for start and stop moments.
try ( BufferedReader reader = Files.newBufferedReader ( p , StandardCharsets.UTF_8 ) ) {
    String lineOdd, lineEven;
    while ( ( lineOdd = reader.readLine () ) != null ) {
        if ( ( lineEven = reader.readLine () ) != null ) {
            LocalDateTime ldtStart = LocalDateTime.parse ( lineOdd , f );
            LocalDateTime ldtStop = LocalDateTime.parse ( lineEven , f );
            Duration duration = Duration.between ( ldtStart , ldtStop );
            long millis = duration.getSeconds ();
            int nanos = duration.getNano ();
            System.out.println ( "ldtStart: " + ldtStart + " | ldtStop: " + ldtStop + " | duration: " + duration + " | Sleep for millis: " + millis + " + nanos: " + nanos );
            //  Thread.sleep ( millis , nanos );
        } else { // Else even-numbered line was null. We expect pairs of input lines for start-stop moments.
            System.out.println ( "ERROR - Failed to find even-numbered line for stop-moment." );
        }
    }
} catch ( IOException x ) {
    System.err.format ( "IOException: %s%n" , x );
}

Given this data:

20 Mar 2012 19:10:33
20 Mar 2012 19:10:34
20 Mar 2012 19:10:41
20 Mar 2012 19:10:55
20 Mar 2012 19:11:02
20 Mar 2012 19:11:22

…when run…

ldtStart: 2012-03-20T19:10:33 | ldtStop: 2012-03-20T19:10:34 | duration: PT1S | Sleep for millis: 1 + nanos: 0

ldtStart: 2012-03-20T19:10:41 | ldtStop: 2012-03-20T19:10:55 | duration: PT14S | Sleep for millis: 14 + nanos: 0

ldtStart: 2012-03-20T19:11:02 | ldtStop: 2012-03-20T19:11:22 | duration: PT20S | Sleep for millis: 20 + nanos: 0

A better way

If your input file was meant to represent a pair of moments on the timeline, then record them as such. The ISO 8601 standard defines an interval as a pair of date-time strings concatenated with a SOLIDUS (slash) character.

And as discussed above, use standard ISO 8601 formats for each date-time value. Always include an offset-from-UTC or time zone with such values. When you intend UTC as the offset, you can simply append a Z, short for Zulu.

Instant

So 20 Mar 2012 19:10:33 becomes 2012-03-20T19:10:33Z. A value such as 2012-03-20T19:10:33Z can be processed as a Instant object. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds.

And the pair in standard format would be 2012-03-20T19:10:33Z/2012-03-20T19:10:34Z. So your input file should be:

2012-03-20T19:10:33Z/2012-03-20T19:10:34Z
2012-03-20T19:10:41Z/2012-03-20T19:10:55Z
2012-03-20T19:11:02Z/2012-03-20T19:11:22Z

Interval

Such interval pairs can be represented as objects by the Interval class found in the ThreeTen-Extra project. This project extends the java.time classes built into Java. This project is also a proving ground for future possible additions to java.time.

Interval interval = Interval.of( start , stop );

The Interval class can parse and generate the lines just shown directly as they comply fully with ISO 8601. No need to specify any formatting patterns.

String output = interval.toString(); // Example: 2012-03-20T19:10:33Z/2012-03-20T19:10:34Z

…or going the other direction…

Interval interval = Interval.parse( "2012-03-20T19:10:33Z/2012-03-20T19:10:34Z" );

You can get a Duration from an Interval.

Duration duration = interval.toDuration();

So what is the difference between Interval and Duration? The first is tied to specific moments on the timeline; the second is not attached to the timeline.

Standard duration format: PnYnMnDTnHnMnS

Similarly, if you want log the duration as a span of time unattached to the timeline, let the Duration class read and write the standard ISO 8601 format for durations PnYnMnDTnHnMnS where the P marks the beginning and the T separates any year-months-days from the hours-minutes-seconds. You see this format in the example output above as generated by the Duration::toString method. For example, PT14S is fourteen seconds.

Duration duration = Duration.between ( start , stop );
String output = duration.toString();  // Example: PT14S

…and going the other direction…

Duration duration = Duration.parse( "PT14S" );

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to java.time.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.

Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154