5
\$\begingroup\$

Is there any better way of printing it?

public class PercentPrinter {

/**
 * @param args
 */
public static void main(String[] args) {

    for (int i = 0; i < 100; i++) {
        if (i == 0)
            System.out.print("percent completed: " + i + " %");
        try {
            Thread.sleep(500);
        } catch (Exception e) {
            System.out.println(e);
        }
        if (i > 9 && i <= 99) {
            System.out.print('\b');
            System.out.print('\b');
            System.out.print('\b');
            System.out.print('\b');

            System.out.print(i + " %");
        } else {
            System.out.print('\b');
            System.out.print('\b');
            System.out.print('\b');

            System.out.print(i + " %");

        }
    }
}
}
\$\endgroup\$
5
\$\begingroup\$

this sounds like a good candidate for a printf:

  • always print a fixed amount of backspaces.
  • always print a fixed-size % value.

Finally, when monitoring things this way you always have a fencepost problem. Your work is the space between the fenceposts, and the fenceposts are the messages you write. You always have one more fencepost than space between them. You will need to have some message outside the loop. In your case, you do 100 items of work, so you will need 101 messages. I tend to chose to print the progress message after the work, so I tend to pre-print the first message before the loop.

Also, you should have a simpler system for mixing the work (the sleep(500)) and the messages(prints).

Consider the setup:

System.out.print("percent completed:   0 %")
for (int i = 0; i < 100; i++) {
    try {
        Thread.sleep(500);
    } catch (Exception e) {
        System.out.println(e);
    }
    System.out.printf("\b\b\b\b\b%3d %%", (i+1) );
}

The above code will pre-print the message, and then each loop iteration it will erase 5 spaces, print a 3-wide integer value (padded with spaces), followed by " %". It requires the appropriate escaping for printf.

Update: A few additional things:

%3d -> You request details on what the %3d does. It prints a minimum 3-character wide string representing an integral amount. if supplied with the integer 0 it will print ' 0' (two spaces and a 0). If supplied with 99 it will print ' 99'. If supplied with more than 3 digits, or more than 2-digit negative numbers, it will use as many characters as it needs. So, for -100 it will print '-100', and for 12345 it will print '12345'. You can read up on the documentation for the Formatter class to see how this works. Examples and tutorials are available online which will help too.

Error Handling - you should do more than just System.out.println(e). There are degrees of error handling, and depending on the conditions there may be reasons to ignore, log, wrap, dump, throw, record, combine, or just count exceptions. But, never should you do a System.out.println(e). This just prints the toString of the exception, and that's not very useful. You should probably at least use e.printStackTrace()

\$\endgroup\$
  • \$\begingroup\$ what is %3d here? \$\endgroup\$ – MaheshVarma Jan 28 '14 at 11:47
  • \$\begingroup\$ As I say, it will will erase 5 spaces, print a 3-wide integer value (padded with spaces), followed by " %" \$\endgroup\$ – rolfl Jan 28 '14 at 12:16
6
\$\begingroup\$

Standing on the shoulders of @rolfl…

I would prefer to use a carriage return to return the cursor to the beginning of the line, as it's a bit less fragile than backspacing.

public static void main(String[] args) {
    // Java's Printwriter.format() doesn't tell you how many characters
    // it produced; 30 characters is an overestimate to ensure that
    // the entire "Percent completed: ***%" string gets overwritten.
    final String CLEARLINE_FMT = "\r%30s\r";

    try {
        for (int i = 0; i < 100; i++) {
            System.out.printf("\rPercent completed: %3d%%", i);
            Thread.sleep(500);
        }
        System.out.printf(CLEARLINE_FMT + "Done!\n", "");
    } catch (InterruptedException e) {
        System.out.printf(CLEARLINE_FMT + "Interrupted!\n", "");
    }
}

Or, if you want to print "100%" instead of "Done!":

public static void main(String[] args) {
    // Java's Printwriter.format() doesn't tell you how many characters
    // it produced; 30 characters is an overestimate to ensure that
    // the entire "Percent completed: ***%" string gets overwritten.
    final String CLEARLINE_FMT = "\r%30s\r";

    try {
        for (int i = 0; ; i++) {
            System.out.printf("\rPercent completed: %3d%%", i);
            if (i >= 100) {
                System.out.println();
                break;
            }
            Thread.sleep(500);
        }
    } catch (InterruptedException e) {
        System.out.printf(CLEARLINE_FMT + "Interrupted!\n", "");
    }
}
\$\endgroup\$
1
\$\begingroup\$

Here's a solution that doesn't require you to have a fixed length string, and also doesn't require much setup/cleanup.

The holder parameter remembers the length of the previous message and clears that many characters the next time something is printed.

public static void printDynamic(AtomicInteger holder, String str) {
    String reset = "";
    if (holder.get() > 0) {
        reset = String.format("%0" + holder.get() + "d", 0).replace('0', '\r');
    }
    System.out.print(reset + str);
    holder.set(str.length());
}

Example usage:

public static void main(String[] args) {
    AtomicInteger holder = new AtomicInteger();
    try {
        for (int i = 0; i <= 100; i++) {
            printDynamic(holder, String.format("%02d%% complete", i));
            Thread.sleep(500);
        }
        printDynamic(holder, "Done\n");
    } catch (InterruptedException e) {
        printDynamic(holder, "Interrupted\n");
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.