必须确保编码与子进程预期字符集一致,否则会导致乱码、命令解析失败或输入截断;需显式用StandardCharsets.UTF_8等常量构造OutputStreamWriter,并调用flush()确保数据发送。

Java 中通过 OutputStreamWriter 向 Process.getOutputStream() 写入数据时,**必须确保编码与子进程预期的字符集一致**,否则可能造成乱码、命令解析失败或输入截断。关键点在于:子进程(如 shell、Python 脚本、自定义程序)默认读取标准输入时使用的编码,通常取决于其运行环境(如系统 locale),而非 Java 的默认编码。
确认子进程实际期望的编码
这是最关键的前置步骤。例如:
- Linux/macOS 的 bash/sh 默认使用 UTF-8(但受
LANG环境变量影响,如LANG=en_US.UTF-8); - Windows 的 cmd.exe 默认使用系统 ANSI 编码(如 GBK/CP936 中文环境,CP1252 英文环境);
- Java 子进程(如另一个 JVM)若用
new Scanner(System.in)且未指定 charset,则按平台默认编码读取; - Python 3 的
sys.stdin在多数情况下默认 UTF-8,但可通过sys.stdin.encoding确认。
建议在启动子进程前显式设置其环境变量(如 env.put("PYTHONIOENCODING", "UTF-8"))或命令行参数(如 python -X utf8 script.py),统一编码预期。
正确构造带指定编码的 OutputStreamWriter
不要依赖 Process.getOutputStream() 的默认行为——它返回的是原始字节流(OutputStream),本身无编码概念。必须显式包装为指定编码的字符流:
立即学习“Java免费学习笔记(深入)”;
Process process = new ProcessBuilder("python", "echo_input.py")
.redirectErrorStream(true)
.start();
<p>// ✅ 正确:明确指定 UTF-8 编码
OutputStreamWriter writer = new OutputStreamWriter(
process.getOutputStream(), StandardCharsets.UTF_8);</p><p>writer.write("你好,世界\n");
writer.flush(); // 必须 flush,否则缓冲区内容可能不立即发送
注意:
- 避免使用 new OutputStreamWriter(out, "UTF-8") 字符串形式编码名,易抛 UnsupportedEncodingException;
- 推荐用 StandardCharsets.UTF_8 等常量,类型安全且无需异常处理。
处理换行符与缓冲策略
子进程是否能及时读到输入,不仅取决于编码,还和换行符及缓冲有关:
- 写入后务必调用
writer.flush()——OutputStreamWriter有内部字符缓冲,不 flush 可能导致数据滞留; - 注意换行符一致性:Unix 用
"\n",Windows 用"\r\n"。多数现代程序(尤其脚本语言)能自动识别"\n",但某些 C 程序或终端模拟器可能严格依赖系统换行; - 若子进程是交互式程序(如
bc,python -i),可考虑调用writer.close()触发 EOF(等价于 Ctrl+D/Ctrl+Z),但需确保子进程已准备好处理 EOF。
验证编码是否生效的简单方法
在子进程中打印接收到的原始字节,比仅看字符串输出更可靠:
- Python 示例:
import sys; data = sys.stdin.buffer.read(); print([b for b in data]); - Bash 示例:
od -t x1或xxd查看 stdin 字节; - 对比 Java 端用
"你好".getBytes(StandardCharsets.UTF_8)输出的字节序列,应完全一致。
若字节不匹配,说明编码链某处出错:可能是 OutputStreamWriter 编码设错、子进程读取时用了错误 charset,或中间有代理(如伪终端、IDE 控制台)做了二次转码。
不复杂但容易忽略:编码必须两端对齐,写入端指定什么,读取端就得用什么解码——没有“自动适配”这回事。


















