Java Process.getInputStream()使用哪种编码?

Java Process.getInputStream()使用哪种编码?,java,character-encoding,Java,Character Encoding,在Java程序中,我通过ProcessBuilder生成一个新的进程 args[0] = directory.getAbsolutePath() + File.separator + program; ProcessBuilder pb = new ProcessBuilder(args); pb.directory(directory); final Process process = pb.start(); 然后,我用一个新的线程 new Thread() { public voi

在Java程序中,我通过
ProcessBuilder
生成一个新的
进程

args[0] = directory.getAbsolutePath() + File.separator + program;
ProcessBuilder pb = new ProcessBuilder(args);
pb.directory(directory);
final Process process = pb.start();
然后,我用一个新的
线程

new Thread() {
    public void run() {
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(process.getInputStream()));
        String line = "";
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
    }
}.start();
但是,当进程输出非ASCII字符(例如
'ee'
)时,
将使用字符
'\uFFFD'

getInputStream
返回的
InputStream
中的编码是什么(我的平台是欧洲的Windows)

如何更改内容以使
包含预期的数据(即
'\u00E9'
'é'

编辑:我尝试了新的InputStreamReader(…,“UTF-8”)

é
变为
\uFFFD

输入流是二进制流,因此没有编码。创建读取器时,需要知道要使用什么字符编码,这取决于调用的程序生成什么(Java不会以任何方式转换它)

如果没有为InputStreamReader指定任何内容,它将使用平台默认编码,这可能不合适。存在允许您指定编码的

如果您知道要使用什么编码(并且您确实必须知道):

根据“\uFFFD”,是字符“é”的unicode代码。这实际上意味着您正在正确地读取流。你的问题是书面的


默认情况下,Windows控制台不支持unicode。所以,如果您想测试您的代码,请打开文件并在那里编写流。但是不要忘记设置编码
UTF-8

据我所知,操作系统流是字节流,这里没有字符。
InputStreamReader
构造函数使用jvm默认字符集
java.nio.charset.charset#defaultCharset()
,您可以使用另一个构造函数显式指定字符集。

我将此作为注释,但我看到后面有一个答案,所以现在可能是多余的:)


有趣的是,在Windows上运行时:

ProcessBuilder pb = new ProcessBuilder("cmd", "/c dir");
Process process = pb.start();
然后CP437代码页对于

new InputStreamReader(process.getInputStream(), "CP437");
科学的 在Windows上,此操作非常完美:

private static final Charset CONSOLE_ENCODING;
static {
    Charset enc = Charset.defaultCharset();
    try {
        String example = "äöüßДŹす";
        String command = File.separatorChar == '/' ? "echo " + example : "cmd.exe /c echo " + example;
        Process exec = Runtime.getRuntime().exec(command);
        InputStream inputStream = exec.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while (exec.isAlive()) {
            Thread.sleep(100);
        }
        byte[] buff = new byte[inputStream.available()];
        if (buff.length > 0) {
            int count = inputStream.read(buff);
            baos.write(buff, 0, count);
        }

        byte[] array = baos.toByteArray();
        for (Charset charset : Charset.availableCharsets().values()) {
            String s = new String(array, charset);
            if (s.equals(example)) {
                enc = charset;
                break;
            }
        }
    } catch (InterruptedException e) {
        throw new Error("Could not determine console charset.", e);
    } catch (IOException e) {
        throw new Error("Could not determine console charset.", e);
    }
    CONSOLE_ENCODING = enc;
}

根据规范:没有jvm运行时编码更改的提示。我们无法确保编码在运行时不会更改,并且更改后字符集仍然正确。

在此使用中使用commons lang jar文件-
StringEscapeUtils.escapeHtml

BufferedReader br = new BufferedReader(
    new InputStreamReader(StringEscapeUtils.escapeHtml(conn.getInputStream()));

如果您和我一样,知道要对所有输入/输出使用什么编码,那么您可以在对某些(不是全部)CreateReader方法的JavaAPI调用中对其进行编码,其他一些答案已经指出了这一点

但这将在源代码中硬编码,这可能行,也可能不行

我在阅读后发现了一个更好的方法,它揭示了您可以在JVM启动之前设置编码,以满足您的需要

java -Dfile.encoding=ISO-8859-1 ...

BufferedReader br=新的BufferedReader(新的InputStreamReader(conn.getInputStream(),“UTF-8”)@Cris如果你想回答,请写一个答案而不是评论,@AlexR指出,同样的推理也适用于数据写入。UTF-8是Java中的默认编码,因此“UTF-8”没有帮助。解决方案很接近,它只需要“Cp1252”或“ISO-8859-1”(取决于
getInputStream()
返回的内容)UTF-8不是Java中的默认编码。根本没有默认设置,它总是使用依赖于平台的东西(可以由环境变量和系统属性控制)。这不是应用程序开发人员通常应该依赖的东西。最好始终明确您想要的编码。UTF-16是java的标准内部字符表示形式。因此产生了无符号16位“char”原语。InputStreamReader将始终转换为UTF-16。尽管InputStream是一个二进制流,但如果它表示字符,则字节将遵循用于创建资源的任何编码。Thilo提到的InputStreamReader构造函数包含一个参数,用于指定该资源的编码-应如何处理流。正确。新的PrintWriter(OutputStreamWriter(…,“Cp1252”)),其中Cp1252是带有Windows扩展名的拉丁语-1,在西欧的一小部分地区(法国、德国和一些国家)使用。当我有字符
0xFFFD
又称“替换字符”时,为什么你要指向字符(
0xE9
,我想要的字符)(…,“ISO-8859-1”)
UTF-8是默认编码。因此,这没有帮助。与其他SAI一样,InputStream在平台编码中包含字符。因为我有现代操作系统,所以我有UTF-8;因为你有Windows,所以你有CP437。谢谢,
CP437
是唯一适合我的字符集名称(Windows+西班牙语字符)事实上,现在应该是CP850。奇怪的是,似乎所有的windows系统都设置为windows-1252/cp1252(至少在西欧),但控制台专门使用CP850。CP437是CP850的前身。打开命令提示符并运行“chcp”应该准确地告诉您打印字符数据时使用的编码。此外,用于解析InputStream的编码取决于ProcessBuilder构建的程序。例如:CP850 for cmd,windows-1252 for其他一些您可以直接调用的windows工具(无需在cmd中包装它们),如果您调用的程序输出UTF-8,则可能是UTF-8。这是特定于程序的,应该在程序的文档中查找。很好!我检查了一些windows 10设置。对于各种欧洲设置,它是CP850,但对于默认设置(美国设置),它仍然是CP437.Hmmm…好主意,但实际上它在我的系统上不起作用(Windows 7 SP1,64位,Java 8 build 71)--所有可用的编码都不会生成原始字符串。问题似乎是给定的示例字符串甚至没有正确地传输到系统中,生成“?”字符
BufferedReader br = new BufferedReader(
    new InputStreamReader(StringEscapeUtils.escapeHtml(conn.getInputStream()));
java -Dfile.encoding=ISO-8859-1 ...