Windows环境下Java Runtime.exec()处理Unicode字符异常的问题咨询
解决Windows下Java Runtime.exec()执行带非英文参数的乱码问题
我来帮你梳理这个问题的根源和解决方案,这其实是Windows系统与Java执行外部命令时的编码交互问题,和你提到的JDK-4947220确实相关,但修复范围有限。
问题1:该问题是否与UTF-8、UTF-16编码格式相关?
是的,核心就是编码不匹配:
- Windows的命令行环境(cmd.exe)默认使用系统的ANSI编码(比如中文系统是GBK,俄文系统是CP1251),而Java的
Runtime.exec(String)方法会将传入的字符串按JVM默认编码(通常是UTF-8)转换为字节数组,再调用Windows的ANSI版本API(CreateProcessA)传递给cmd.exe,导致非英文字符转码错误。 - 你提到的JDK-4947220修复的是文件名的编码问题(Java会用
sun.jnu.encoding属性对应编码处理文件名,该编码在Windows下是UTF-16,匹配系统文件名编码),但参数传递的编码问题在Java 8的Windows环境下依然存在。
问题2:如何解决该问题?不希望采用危险且不优雅的临时方案。
推荐两种正规、可靠的解决方案,避免编码转换的坑:
方案1:使用ProcessBuilder传递参数数组(优先推荐)
ProcessBuilder在Windows下会直接调用宽字符版本的API(CreateProcessW),无需编码转换就能传递Unicode字符串,彻底避免乱码问题。同时拆分命令为数组,还能避免字符串分割带来的其他问题(比如参数含空格的情况)。
修改后的测试代码:
import java.io.BufferedReader; import java.io.InputStreamReader; public class MainClass { private static void foo(String[] cmdArray) { try { // 获取系统默认的命令行输出编码,确保读取输出时编码匹配 String systemEncoding = System.getProperty("sun.jnu.encoding"); ProcessBuilder pb = new ProcessBuilder(cmdArray); pb.redirectErrorStream(true); // 合并错误流到输入流,方便统一读取 Process process = pb.start(); BufferedReader input = new BufferedReader( new InputStreamReader(process.getInputStream(), systemEncoding)); String line; while ((line = input.readLine()) != null) { System.out.println(line); } input.close(); process.waitFor(); // 等待外部命令执行完成 } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { foo(new String[]{"你好.bat", "你好"}); foo(new String[]{"привет.bat", "привет"}); foo(new String[]{"hi.bat", "hi"}); } }
方案2:强制cmd.exe使用UTF-16编码执行命令
如果你必须使用字符串形式的命令,可以通过cmd /u /c参数强制cmd.exe输出UTF-16LE编码,同时Java读取输出时指定UTF-16LE编码:
private static void foo(String cmd) { try { ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/u", "/c", cmd); pb.redirectErrorStream(true); Process process = pb.start(); // cmd /u会输出UTF-16LE编码的内容 BufferedReader input = new BufferedReader( new InputStreamReader(process.getInputStream(), "UTF-16LE")); String line; while ((line = input.readLine()) != null) { System.out.println(line); } input.close(); process.waitFor(); } catch (Exception e) { e.printStackTrace(); } }
这种方式也能解决乱码,但不如方案1直接,因为还是依赖cmd.exe的编码转换。
问题3:为何批处理文件名可被正常识别,但传入的参数却出现乱码?是否与@echo有关?
和@echo完全无关,问题出在文件名和参数的处理路径不同:
- Java处理文件名时,使用的是
sun.jnu.encoding属性对应的编码(Windows下是UTF-16),和系统的文件名编码完全匹配,所以“你好.bat”“привет.bat”能被正确找到并执行。 - 而参数传递时,
Runtime.exec(String)会将整个命令字符串按JVM默认编码(UTF-8)转成字节数组,交给cmd.exe后,cmd.exe用系统ANSI编码(比如GBK/CP1251)解析,导致参数乱码。换句话说,参数在传递到批处理程序之前就已经是乱码了,@echo只是输出了乱码的内容而已。
内容的提问来源于stack exchange,提问作者Andrew Kuleshov
相关产品推荐
相关产品推荐

