Java从System.in读取带重音字符异常的问题排查求助
问题背景
开发需从控制台读取含重音字符的Java应用时,发现现代控制台(如Windows Terminal中的PowerShell)本身可正确处理重音字符输入输出:
PS C:\> echo ? ü Ps C:\>
注:该行为在单独的命令提示符中无法复现;但在Windows Terminal中运行命令提示符时,发送前即可正确显示重音字符。
但运行测试代码时出现异常,输入的重音字符字节数组输出全为0。测试代码如下:
package com.test.outputtest; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; import java.nio.file.*; public class OutputTest { public static void main(String[] args) { // Set I/O to use UTF-8 System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8)); // Create the response listener Scanner input = new Scanner(System.in, StandardCharsets.UTF_8); System.out.println(Arrays.toString("èéëê".getBytes(StandardCharsets.UTF_8))); String temp = input.nextLine(); System.out.println(Arrays.toString(temp.getBytes(StandardCharsets.UTF_8))); } }
生成app.jar后,在PowerShell执行命令及输出:
PS C:\Users\[name]\Desktop\output_test> chcp 65001 Active code page: 65001 PS C:\Users\[name]\Desktop\output_test> java "-Dfile.encoding=UTF-8" -jar app.jar [-61, -88, -61, -87, -61, -85, -61, -86] èéëê [0, 0, 0, 0]
尝试用Console替代Scanner问题依旧;IntelliJ终端中运行正常,但命令提示符或PowerShell中均报错。
环境信息:
- 操作系统: Windows 10 build 19045.2728
- Java版本: 17.0.6(IntelliJ中使用同版本)
- 默认代码页: 850(OEM)
- 测试时代码页: 65001(UTF-8)
问题原因
Windows控制台的UTF-8支持存在历史兼容问题,Java处理控制台输入流时,即使指定UTF-8编码,也可能因底层Windows API的编码映射机制出错;同时手动替换System.out的PrintStream会干扰控制台编码一致性。
解决方案
1. 移除手动替换System.out的代码
删除代码中手动设置System.out的行,依赖JVM参数统一编码:
// 移除该行代码 // System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8));
启动命令保持java "-Dfile.encoding=UTF-8" -jar app.jar,JVM会统一处理标准输入输出的编码。
2. 开启Windows原生UTF-8全局支持
修改系统设置让控制台默认使用UTF-8:
- 打开「设置」→「时间和语言」→「语言」→「管理语言设置」→「更改系统区域设置」
- 勾选「Beta版:使用UTF-8提供全球语言支持」,重启系统
此设置无需手动执行chcp 65001,同时解决Java输入的编码映射问题。
3. 临时兼容方案(不推荐长期使用)
若无法修改系统设置,可临时切换到ANSI编码规避问题:
- 执行
chcp 1252切换编码 - 修改代码中Scanner的编码为
StandardCharsets.ISO_8859_1
注意:UTF-8是现代本地化标准,长期使用ANSI会限制多语言字符支持。
4. 正确使用Console类
使用Console类直接读取字符,避免编码转换错误:
Console console = System.console(); if (console != null) { String input = console.readLine(); System.out.println(Arrays.toString(input.getBytes(StandardCharsets.UTF_8))); }
验证
修改代码后重新打包,在PowerShell执行启动命令,输入重音字符后,输出的字节数组应与预定义字符串的字节数组一致(如输入èéëê会输出[-61, -88, -61, -87, -61, -85, -61, -86])。
内容的提问来源于stack exchange,提问作者ShadeOfLight

