【问题标题】:Which encoding does Process.getInputStream() use?Process.getInputStream() 使用哪种编码?
【发布时间】:2012-01-13 23:00:21
【问题描述】:

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

args[0] = directory.getAbsolutePath() + File.separator + program;
ProcessBuilder pb = new ProcessBuilder(args);
pb.directory(directory);
final Process process = pb.start();

然后,我用新的Thread 读取进程标准输出

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 字符(例如 'é')时,line 将具有字符 '\uFFFD'

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

如何更改内容以使line 包含预期数据(即'\u00E9' 对应'é')?

编辑:我试过new InputStreamReader(...,"UTF-8")é 变为 \uFFFD

【问题讨论】:

  • BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
  • @Cris 如果你想回答,请写一个答案而不是评论

标签: java character-encoding


【解决方案1】:

InputStream 是二进制流,因此没有编码。创建 Reader 时,您需要知道要使用什么字符编码,这取决于您调用的程序产生的内容(Java 不会以任何方式对其进行转换)。

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

如果你知道要使用什么编码(而且你真的必须知道):

new InputStreamReader(process.getInputStream(), "UTF-8") // for example

【讨论】:

  • 正如@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 构造函数包含一个参数来指定该资源的编码 - 应该如何处理流。
【解决方案2】:

根据http://www.fileformat.info/info/unicode/char/e9/index.htm '\uFFFD' 是字符'é' 的 unicode 代码。这实际上意味着您正在正确读取流。你的问题是书面的。

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

【讨论】:

  • 正确。 new PrintWriter(OutputStreamWriter(..., "Cp1252")) 其中 Cp1252 是带有 Windows 扩展的 Latin-1,用于西欧的一小部分(法国、德国和一些)。
  • 当我有字符 0xFFFD aka 'REPLACEMENT CHARACTER' fileformat.info/info/unicode/char/fffd/index.htm时,你为什么要指向字符(我想要的0xE9
【解决方案3】:

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

【讨论】:

  • 是的,我必须new InputStreamReader(...,"ISO-8859-1")
【解决方案4】:

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

BufferedReader br = new BufferedReader(
    new InputStreamReader(conn.getInputStream(), "UTF-8"));

【讨论】:

  • UTF-8 是默认编码。所以,这没有帮助。
【解决方案5】:

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

ProcessBuilder pb = new ProcessBuilder("cmd", "/c dir");
Process process = pb.start();

然后 CP437 代码页非常适合

new InputStreamReader(process.getInputStream(), "CP437");

【讨论】:

  • 与其他 sais 一样,InputStream 包含平台编码中的字符。因为我有一个现代操作系统,所以我有 UTF-8;既然你有 Windows,你就有 CP437。
  • 谢谢,CP437 是唯一对我有用的字符集名称(Windows + 西班牙字符)
  • 其实现在应该是CP850了。奇怪的是,似乎所有的 Windows 系统都设置为 windows-1252/cp1252(至少在西欧),但控制台专门使用 CP850。 CP437是CP850的始祖。打开命令提示符并运行“chcp”应该会告诉您它使用哪种编码来打印字符数据。
  • 此外,用于解析 InputStream 的编码取决于 ProcessBuilder 所构建的程序。例如:CP850 用于 cmd,windows-1252 用于您可能直接调用的其他一些 Windows 工具(不将它们包装在 cmd 中),如果您调用的程序输出 UTF-8,则可能是 UTF-8。这是特定于程序的,应该在程序的文档中查找。
  • 不错!我检查了一些 Windows 10 设置。对于各种欧洲设置,它是 CP850,但对于默认值(美国设置),它仍然是 CP437。
【解决方案6】:

科学

在 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的运行时编码更改。我们不能确保编码在运行时不会改变,并且在这种改变之后字符集仍然正确。

【讨论】:

  • 嗯...好主意,但它实际上不适用于我的系统(Windows 7 SP1,64 位,Java 8 build 71)——没有可用的编码产生原始细绳。问题似乎是给定的示例字符串甚至没有正确传输到系统,产生“?”而是字符。除此之外,我还在输出中获得了一个额外的“\r\n”结束行。
【解决方案7】:

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

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

【讨论】:

    【解决方案8】:

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

    但这会在源代码中对其进行硬编码,这可能会也可能不会。

    我在阅读this answer 后发现了一种更好的方法,它表明您可以在 JVM 启动之前将编码设置为您需要的。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-20
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-06
      相关资源
      最近更新 更多