Java 中的暂停打印无法正常工作

12

我一直在尝试使用Java编写的程序逐字输出文本,并在每个字母之间暂停。 该代码将字符串进行单词换行并打印出来。我的延迟方法“slow()”在延迟为半秒或一秒时效果很好,但在较低的延迟时间下它会出现一些奇怪的问题。

当打印和延迟时间过短时,程序会在该行挂起,直到延迟时间乘以要打印的字母数,然后一次性输出所有内容。

另外,当将延迟设置为250毫秒时,文本也会打印错误。

在此示例中,字符串为:

 

"Lorem ipsum dolor sit amet,consectetur adipiscing elit。Nulla vitae molestie leo,sed molestie turpis."

预期输出:

 

Lorem ipsum dolor sit amet,consectetur adipiscing
  elit。Nulla vitae molestie leo,sed molestie turpis。

但是在250时的输出结果如下:

 

Lrem ipsum dolrst aet,conseteur adipiscing
  elit。ulla vitae olestie lo sed oleste turis。

以下是代码:

public static void main(String[] args) {
    String x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae molestie leo, sed molestie turpis.";
    say(500,x); // Works Nicely, does one letter at a time with a 0.5s wait in between.
    System.out.println();
    say(250,x); // Has proper delay, but prints strange stuff
    System.out.println();
    say(100,x); // Prints Line by line with a wait of (letters*0.1s) wait in between.
}

public static void say(int speed, String words) {
    int i = 0;
    int ii = 0;
    while (i < words.length()) {
        slow(speed);
        System.out.print("" + words.charAt(i));
        if (ii >= 50 && words.charAt(i) == ' ') {
            System.out.println(words.charAt(i));
            ii = 0;
        } else {
            ii++;
        }
        i++;
    }
    System.out.println(" ");
}

public static void slow(int time) {
    try {
        Thread.sleep(time);
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }
}

另一种具有相同缺陷的slow()方法:

public static void slow(int time) {
    long startTime = System.currentTimeMillis();
    while(System.currentTimeMillis() - startTime < time) {

    }
}

我不确定这是否重要,但所有操作都在NetBeans 7.4 x64和JDK 1.7下完成。

虽然我是Java新手,但对编程并不陌生。感激任何帮助!主要问题是时间控制, 这是我需要解决的;奇怪的输出只是一个疑问。


好问题,有点长... 这是一些有趣的行为... - Anubian Noob
3
我还没有测试过(最好提供可编译的示例),但可以尝试在打印单个字母后(等待之前)使用 System.out.flush()。请注意,这是一种建议,不保证一定有效。 - Marco13
1
在Eclipse中,无论是否调用flush(),我的程序都可以正常工作。 - Jim Garrison
1
它在Eclipse中运行得很好,也可以作为一个jar文件从命令行调用。你的代码没有问题,一定是你的环境出了问题。 - Jim Garrison
1
在Netbeans控制台中无法工作,可能是因为它使用BufferedReader来读取Java进程的输出。但在控制台中可以正常工作。不过已将System.out.println(words.charAt(i));更改为System.out.println(); - MadProgrammer
显示剩余5条评论
2个回答

1
这看起来有些可疑..你的代码看起来很好,我从命令行运行它也没问题。我认为,就像吉姆·加里森所说,这与你的环境有关。你是否尝试过从命令行运行程序,以避免使用Netbeans?
接下来我怀疑的是你catch (InterruptedException ex)块中的Thread.currentThread().interrupt();行。尝试将其注释掉(除了可能打印InterruptedException堆栈跟踪之外,不要做其他任何事情)。

0

使用Timer/TimerTask的惯用解决方案:

需要注意的惯用方法:

  1. 使用BreakIterator将文本分开。
  2. 使用Timer/TimerTask来管理延迟。
  3. 使用AtomicLong来保证线程安全的共享对象。

Q23794090.java

import javax.annotation.Nonnull;
import java.text.BreakIterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicLong;

public class Q23794090
{
    private static final Timer TIMER = new Timer();
    private static AtomicLong DELAY = new AtomicLong(0L);

    private static void say(final long delay, @Nonnull final String value, @Nonnull final BreakIterator bi)
    {
        bi.setText(value);
        int start = bi.first();
        int end = bi.next();

        while (end != BreakIterator.DONE)
        {
            final String s = value.substring(start, end);
            TIMER.schedule(new TimerTask()
            {
                @Override
                public void run()
                {
                    System.out.print(s);
                }
            }, DELAY.addAndGet(delay));
            start = end;
            end = bi.next();
        }
        TIMER.schedule(new TimerTask()
        {
            @Override
            public void run()
            {
                System.out.println();
                System.out.println("========================");
            }
        }, DELAY.addAndGet(0));
    }

    public static void main(final String[] args)
    {
        final String x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae molestie leo, sed molestie turpis.";
        say(250, x, BreakIterator.getCharacterInstance());
        say(500, x, BreakIterator.getWordInstance());
        say(1000, x, BreakIterator.getSentenceInstance());
        TIMER.schedule(new TimerTask()
        {
            @Override
            public void run()
            {
                TIMER.cancel();
            }
        }, DELAY.addAndGet(1000));
        try { Thread.currentThread().join(); } 
        catch (InterruptedException e) { throw new RuntimeException(e); }
    }
}

适用于Intellij Idea 13.x

在控制台中,所以不需要调用System.out.flush()之类的任何废话。

需要改进的地方:

  1. BreakIterator 工厂方法可以使用可选的 Locale,因为我很难找到一个 Latin 的方法,所以我让它使用默认值。在我的情况下,这是 en_US。你的情况可能会有所不同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接