Process.getInputStream() 使用哪种编码?
Posted
技术标签:
【中文标题】Process.getInputStream() 使用哪种编码?【英文标题】:Which encoding does Process.getInputStream() use? 【发布时间】: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 如果你想回答,请写一个答案而不是评论 【参考方案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 ...
【讨论】:
以上是关于Process.getInputStream() 使用哪种编码?的主要内容,如果未能解决你的问题,请参考以下文章