当前位置: 首页 > 知识库问答 >
问题:

JavaBufferedWriter(输出流作家)没有检测到输出关闭(例如manyline|java...|head-3)

金成济
2023-03-14

我想写一个过滤程序,可以在stdin上读取千兆字节的行(日志),并将行写入stdout,但到目前为止,我一直无法让它在输出关闭时停止。

在下面的调用中,当head-3完成并关闭其输入时,java进程不会终止。我看到前三行被发出,然后它挂起(大概直到zcat | java部分完成,它就会挂起)。

zcat blah/*.gz | java -cp . LikeCat | head -3

我希望它的行为如下所示,当head完成时,整个管道关闭。

zcat blah/*.gz | cat | head -3

为了进行比较(我在找出从Java中的stdin中读取行的最佳方式时,主要是为了进行性能比较),我使用了以下Python,它可以按预期工作:

#!/usr/bin/env python2.7

import fileinput

for line in fileinput.input():
    print line

但我的Java foo显然太没意思了。这就是我目前所拥有的。没有例外被打印出来。到目前为止,超过100万行的性能相当于Python代码(可能有点令人失望,但超出了这个问题的范围)。

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;

public class LikeCat
{

    public static void main(String[] args) throws IOException
    {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
        String line;

        try {
            while ( (line = in.readLine()) != null ) {
                out.write(line);
                out.newLine();
                // out.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            out.close();
        }
    }
}

通过strace运行它,我发现写入系统调用return-1和errno是EPIPE,正如预期的那样,但查看文档时,我预期会引发IOException。我仍然在思特莱斯,我看到它回到了阅读标准文本。仔细看,它确实查找了一些区域设置数据,因此可能是对某种事件做出了响应,但没有任何异常迹象。

[pid 22649] read(0, ONE
"ONE\n", 8192)      = 4
[pid 22649] write(1, "ONE\n", 4ONE
)        = 4
[pid 22649] read(0, TWO
"TWO\n", 8192)      = 4
[pid 22649] write(1, "TWO\n", 4TWO
)        = 4
[pid 22649] read(0, THREE
"THREE\n", 8192)    = 6
[pid 22649] write(1, "THREE\n", 6)      = -1 EPIPE (Broken pipe)
[pid 22649] --- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=22648, si_uid=1000} ---
[pid 22649] open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 4
[pid 22649] read(4, "# Locale name alias data base.\n#"..., 4096) = 2502
[pid 22649] read(4, "", 4096)           = 0
[pid 22649] open("/usr/share/locale/en_NZ.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 22649] open("/usr/share/locale/en_NZ.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 22649] open("/usr/share/locale/en_NZ/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 22649] open("/usr/share/locale/en.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 22649] open("/usr/share/locale/en.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 22649] open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 22649] read(3, "PK\3\4\n\0\0\10\0\0i\n\206J\34&\t\276\266\1\0\0\266\1\0\0$\0\0\0", 30) = 30
[pid 22649] read(3, "\312\376\272\276\0\0\0004\0\31\n\0\5\0\24\t\0\4\0\25\n\0\5\0\26\7\0\27\7\0\30\1"..., 438) = 438
[pid 22649] write(1, "THREE\n", 6)      = -1 EPIPE (Broken pipe)
[pid 22649] --- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=22648, si_uid=1000} ---
[pid 22649] read(0, 

查看BufferedWriter的文档,它返回void,因此它不是检查返回代码的情况,而且我找不到任何方法来询问“您是否仍然打开?”。

在I^C之前,不会引发IOExctive。

这是Linux上的Oracle Java 1.8。

$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b31)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b31, mixed mode)

谢谢你的帮助干杯Cameron

感谢@EJP指出了系统。out是一个PrintStream,它接受异常,我需要运行系统。出来定期检查错误()。

从留档为System.out.check错误()

public boolean checkError()刷新流并检查其错误状态。当底层输出流引发InterruptedIOException以外的IOException时,以及调用setError方法时,内部错误状态设置为true。[...].

返回:当且仅当此流遇到InterruptedIOException以外的IOException,或者调用了setError方法时,返回true

这是可行的,但很烦人,因为如果我把输出放入循环中,每次都会刷新输出。

我知道它想回答“你写了所有的数据吗?”,但我更感兴趣的是“你是不是碰巧发现你的输出被关闭了?”

如果我偶尔使用'System.out.checkError()'(就像,每100行输入),那么通过程序退出会很慢——如果程序是某个更大管道的一部分,这可能是一件烦人的事情。

如果我使用“系统”。出来checkError()'在每次迭代中,由于checkError()在每次迭代中刷新输出,我都会受到无法忍受的性能损失。

背景让我觉得这里的关键要点是“不要使用System.out,而是使用它下面的原始文件描述符”。

(我删除了这一点,以防止公布一个坏主意。)

幸亏https://stackoverflow.com/a/5339512/1002016,我能够确定是的,我可以创建一个对象(类似于System.out),但基于文件描述符(实际上是FileDescriptor)的。使用FileDescriptor。out获取表示与stdout对应的文件描述符的对象,然后遍历FileOutputStream和OutputStreamWriter,然后是BufferedWriter。

...(不是试图巨魔,但为什么这比FILE*out=fdopen(2)更痛苦?)

这是我现在的代码。它将捕获IOExctive,但是它还没有做任何事情来确定它是否是由于破裂的管道造成的。

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.io.OutputStream;
import java.io.FileDescriptor;
import java.io.FileOutputStream;

public class LikeCat
{
    public static void main(String[] args) throws IOException
    {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(FileDescriptor.out)));
        String line;

        try {
            while ( (line = in.readLine()) != null ) {
                out.write(line);
                out.newLine();
            }
        } catch (IOException e) {
        }
        finally {
            try { out.close(); } catch (IOException e) {}
        }
    }
}

共有1个答案

吴和硕
2023-03-14

System.out是一个PrintStreamPrintStream吞噬异常。您需要定期调用System.out.checkError()

 类似资料:
  • 本小节将会介绍基本输入输出的 Java 标准类,通过本小节的学习,你将了解到什么是输入和输入,什么是流;输入输出流的应用场景,File类的使用,什么是文件,Java 提供的输入输出流相关 API 等内容。 1. 什么是输入和输出(I / O) 1.1 基本概念 输入/输出这个概念,对于计算机相关专业的同学并不陌生,在计算中,输入/输出(Input / Output,缩写为 I / O)是信息处理系

  • 问题内容: 我下面有以下代码示例。你可以在其中输入的命令,即回显结果。但是,先读后。其他输出流不起作用? 为什么会这样或我做错了什么?我的最终目标是创建一个线程计划任务,该任务定期执行对/ bash的命令,因此必须一前一后工作,而不能停止工作。我也一直在经历错误的任何想法? 谢谢。 问题答案: 首先,我建议更换生产线 与线 ProcessBuilder是Java 5中的新增功能,它使运行外部进程更

  • 本文向大家介绍JAVA输出流与输入流代码实例,包括了JAVA输出流与输入流代码实例的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了JAVA输出流与输入流代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 输出流 编程入门的第一个程序,输出一串字符串 输入流 输入流需要引用包的概念,包里面存放的是类。输入流需要实例化InputStr

  • 问题内容: Java中的InputStreams和OutputStreams是否在销毁时关闭()?我完全理解这可能是错误的形式(尤其是在C和C ++世界中),但是我很好奇。 另外,假设我有以下代码: 无名的FileInputStream是否在p.load()之后超出范围,并因此被破坏,就像C ++范围规则一样?我尝试在Google上搜索Java的匿名变量范围,但这并没有达到我的预期。 谢谢。 问题

  • 问题内容: 我应该负责关闭(或或什至),还是应该将其留给容器? 问题答案: 您确实不需要这样做。 经验法则:如果您没有使用自己创建/打开它,则不需要自己关闭它。例如,如果它是一个,那么您显然需要自己关闭它。 有些人仍然这样做的原因仅仅是为了确保不再将任何内容写入响应主体。如果发生这种情况,则将日志中导致,但这不会影响客户端,因此客户端仍会获得正确的响应。这也是一种更容易的调试,可以发现请求-响应链

  • 我想用java代码调用一个外部程序,然后Google告诉我Runtime或ProcessBuilder可以帮助我完成这项工作。我试过了,结果发现java程序无法退出,这意味着子进程和父进程都将永远等待。它们要么挂起,要么陷入僵局。 有人告诉我原因是子进程的缓存太小了。当它试图将数据返回给父进程时,但是父进程没有及时读取它,然后他们两个都挂起了。所以他们建议我叉一个线程来负责读取子进程的缓存数据。我