说实话,VS Code终端里汉字打印成"???"这种破事,我前前后后帮同事和朋友排了七八次。每次看到别人对着屏幕上一排问号发懵,我就知道又是编码问题在作妖。这个问题的坑点在于它不固定——同样的代码,有人在Windows上跑得好好的,有人一打开终端全是问号;有人在VS Code里正常,切到系统CMD又乱了。今天我把这个问题的完整解法整理出来,从现象、原理到不同场景下的处理方案,一篇文章讲透。
如果你的日常工作涉及Python脚本、C/C++程序、Java项目,或者经常在VS Code终端里跑各种命令,这篇文章能帮你省下大量排查时间。就算你是个刚入门的新手,照着下面的步骤一步步来,也能把终端输出汉字乱的毛病治好。
1. 先搞清楚"???"是从哪一步冒出来的
1.1 你看到的问号其实分两种
终端里出现问号,一定要先区分是哪种情况,因为处理方向完全不一样。
第一种是实打实的问号字符。你在终端里看到的是???,而且数量跟汉字数量对得上,一个汉字对应一个问号。这种一般是程序在输出的时候,自己就把非ASCII字符替换成了?,终端其实没做错什么,问题出在程序输出的那一端。
第二种是显示成问号形状的方块或者乱码。有时候终端显示的不是???,而是像涓枃、鏂囨湰这种奇怪字符,或者一个个小方块(豆腐块)。这属于终端用错误的编码去解码了UTF-8字节流,解码出来全是无效字符。
搞清楚这一点很重要,因为网上很多教程只教你改终端编码,但如果你遇到的是第一种情况,改终端编码毫无卵用。我见过有人折腾了半天设置,最后发现是他的Python代码里print函数自己把中文替换成问号了。
1.2 为什么会走到这一步:编码链路拆解
要理解这个问题,得先明白一条完整的输出链路:程序内部字符串 → 编码成字节 → 终端解码 → 字体渲染。
程序里的字符串在内存里是有编码的(Python 3默认Unicode,C/C++要看编译器设置)。当程序往标准输出写数据时,它会按照某个编码把Unicode字符串转成字节序列。这个编码在Windows上默认是GBK(代码页936),在Linux/macOS上默认是UTF-8。
终端收到字节序列后,又要按照它自己的编码去解码。Windows的CMD默认用GBK解码,VS Code的集成终端则取决于它的配置文件。如果程序输出用的是UTF-8,而终端用GBK去解,就会得到"涓枃"这种乱码;如果程序输出时直接把无法编码的字符替换成?,终端解出来的就是整齐的???。
最后一步是字体渲染。就算字节解码成功了,如果终端用的字体里没有对应的字形,也会显示成方块或者问号形状。这一步很多人忽略,但踩坑率极高。
所以,解法要从这三条链路分别下手:要么改程序输出编码,要么改终端解码编码,要么换字体。下面我按从简单到彻底的顺序,把各种方案都写上。
2. 最常用的修复套路:改VS Code终端编码
2.1 一条命令临时切换代码页
如果你只是临时跑个脚本,最快的方法是直接在VS Code终端里执行:
chcp 65001chcp是Windows下切换代码页的命令,65001代表UTF-8。执行完这条命令,当前终端窗口的解码方式就切成UTF-8了,再运行你的程序,中文输出大概率就正常了。
注意,这个命令只对当前终端会话有效。你关掉这个终端标签页,或者新开一个终端,又恢复原样了。而且有个小坑:执行chcp 65001之后,如果程序输出的是GBK编码的字符,反而会乱得更厉害。所以你得先确认程序输出的是什么编码。
如果你用的是PowerShell,还可以用另一种方式:
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8这条命令把控制台的输出编码也切成UTF-8。两种方式可以配合使用,实测在VS Code的PowerShell终端里,这两条命令一起执行后,Python输出中文基本就正常了。
2.2 让VS Code每次打开终端都自动切到UTF-8
每次手动敲chcp 65001太麻烦了,而且容易忘。我推荐直接改VS Code的配置文件,让每个终端标签页打开时自动执行切换命令。
按Ctrl+Shift+P打开命令面板,输入settings.json,打开用户设置文件,在里面加上这一段:
"terminal.integrated.profiles.windows": { "Command Prompt": { "path": "C:\\Windows\\System32\\cmd.exe", "args": ["/K", "chcp 65001 >nul"] }, "PowerShell": { "source": "PowerShell", "args": ["-NoExit", "-Command", "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; [Console]::InputEncoding = [System.Text.Encoding]::UTF8"] } }, "terminal.integrated.defaultProfile.windows": "Command Prompt"/K参数的意思是执行完后面的命令后保持窗口不关闭,>nul是抑制chcp的提示输出,不然每次开终端都先蹦出一行"Active code page: 65001",也挺烦的。
设置完之后,重启VS Code或者手动关掉终端标签页再重新打开,每次都会自动切到UTF-8了。这一招是我实际用得最多的方案,配合下面的程序端设置,基本能覆盖90%的场景。
2.3 设置默认配置文件与字体兜底
编码切对了,字体也得跟上。VS Code默认的终端字体在Windows上通常是Consolas,这个字体对中文的显示支持其实很一般,遇到生僻字或者某些特殊符号,容易显示成方块。
我建议在settings.json里把终端字体改成支持中文的字体:
"terminal.integrated.fontFamily": "Cascadia Code, Microsoft YaHei, Consolas, 'Courier New', monospace"Cascadia Code是微软为终端设计的等宽字体,对中日韩字符做了fallback处理,会把中文字形交给Microsoft YaHei(微软雅黑)渲染。这样既保持了西文字符的等宽效果,中文部分也不至于变成豆腐块。
还有一个容易被忽略的设置:terminal.integrated.fontFamily如果是空的或者只设置了西文字体,终端在渲染中文时会用系统默认字体,这个默认字体在某些精简版Windows系统上可能就是缺失的。所以把微软雅黑加进字体列表做兜底,是最稳妥的做法。
字体这块我踩过的坑是:在Windows Server上远程连接VS Code,服务器上没装微软雅黑,终端中文直接变成一个个空心方块。后来在服务器上装了字体才解决。所以如果你是在服务器环境用VS Code,排查编码问题之前先看一眼系统字体齐不齐。
3. 从程序输出端根治:不同语言场景的对应解法
3.1 Python脚本输出中文变成问号
Python 3的字符串本身是Unicode,问题出在输出环节的编码。在Windows上,Python默认会按照控制台的代码页来编码输出,如果你用的是GBK代码页(936),而字符串里含有GBK无法编码的字符,Python会抛UnicodeEncodeError。但如果你的代码或者框架里做了errors='replace'处理,它就会一声不吭地把字符替换成?打印出来。
解决方法有几个,按优先级来:
第一种,运行前设置环境变量:
set PYTHONIOENCODING=utf-8 python your_script.pyPYTHONIOENCODING是Python读取标准输入输出时使用的编码,设置为utf-8后,print输出的字节流就是UTF-8编码。这个方案不用改代码,适合临时跑脚本。
第二种,在代码里强制重配标准输出:
import sys sys.stdout.reconfigure(encoding='utf-8') print("中文输出")reconfigure是Python 3.7+提供的方法,可以随时修改标准输出的编码。这个方案的好处是只影响你自己程序的输出,不会污染别的程序。
第三种,一劳永逸的做法,在环境变量里永久设置:
在Windows系统属性里添加用户环境变量PYTHONIOENCODING,值为utf-8。这样所有Python脚本的输出默认都是UTF-8编码,不用每次手动设置。副作用是如果你的程序要跟GBK编码的文件交互,输出可能对不上,但绝大多数场景下UTF-8是没问题的。
还有一个Python特有的坑:如果你用的是Python 2(虽然现在很少见了),字符串默认是字节串,遇到中文会直接乱掉。Python 2的解法是在文件开头加:
# -*- coding: utf-8 -*-以及把字符串声明成u"中文"。但现在都2025年了,如果还有老项目跑Python 2,我建议优先考虑升级迁移,而不是继续填编码的坑。
3.2 C/C++程序在终端输出中文乱码
C/C++场景稍微复杂,因为涉及源码文件自身的编码、编译器的编码选项、运行时的代码页设置,三个环节任何一环出问题都会导致乱码。
先说源码文件编码。VS Code默认用UTF-8保存文件,但MSVC编译器(cl.exe)默认认为源代码是本地代码页编码(GBK)。如果两者不一致,编译器解析字符串字面量时就会出错,或者编译出来的程序里的中文字符串本身就是错的。
Visual Studio的MSVC可以用/utf-8参数指定源码编码:
cl /utf-8 your_program.c如果是GCC/Clang,用:
gcc -finput-charset=UTF-8 -fexec-charset=UTF-8 your_program.c -o your_program-finput-charset告诉编译器源文件是UTF-8编码,-fexec-charset告诉编译器把字符串字面量以UTF-8编码存进可执行文件。
然后是运行时的输出编码。Windows的控制台程序可以使用Windows API来设置输出代码页:
#include <windows.h> int main() { SetConsoleOutputCP(CP_UTF8); // 65001 SetConsoleCP(CP_UTF8); printf("中文输出\n"); return 0; }SetConsoleOutputCP函数的作用是让当前进程的输出使用UTF-8编码,这样配合终端侧的UTF-8解码,中文就不会乱。注意,这个API只在Windows上有效,跨平台代码需要加上条件编译。
Linux/macOS下的C/C++程序基本上默认UTF-8编码,很少遇到中文乱码问题。如果遇到了,先检查系统locale用的是不是en_US.UTF-8或zh_CN.UTF-8:
locale如果输出不是UTF-8结尾,可以用:
export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8在bash配置里加上这两行,重启终端生效。
3.3 Java与Node.js场景的编码处理
Java程序在终端输出中文乱码,最常见的原因是JVM的默认字符集跟终端不一致。Java 18之前,JVM默认字符集由系统决定,Windows上通常是GBK;Java 18开始默认变成UTF-8,但老项目可能还带着旧的启动参数。
输出乱码时,首先检查启动命令:
java -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 YourClassfile.encoding控制文件的读写编码,sun.jnu.encoding控制文件名和标准IO的编码。两个都设成UTF-8,基本能解决输出中文乱码的问题。
另外提一个很常见的坑:Maven或Gradle构建工具在编译时用的编码可能跟运行时不一致,导致编译后的class文件里中文字符串本身就是错的。解决办法是在pom.xml里显式声明编码:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties>Gradle则在build.gradle里加:
tasks.withType(JavaCompile) { options.encoding = 'UTF-8' }Node.js场景比较有意思。Node.js的console.log输出默认是UTF-8,在VS Code终端里通常不会乱。如果你用的是Windows的CMD原生窗口,有时会因为CMD的代码页问题显示乱码。这种情况直接chcp 65001就行。如果是在VS Code终端里跑Node.js项目遇到乱码,多半是文件读写的编码问题,检查一下读取文件时有没有指定正确的编码:
const fs = require('fs'); const text = fs.readFileSync('file.txt', 'utf-8');没指定编码时,readFileSync返回的是Buffer,直接打印会显示成十六进制字节或者乱码。这是新手常犯的错误。
另外一个隐藏场景是终端复用工具,比如某些终端模拟器会强制把输出转换成当前会话的编码,如果你在VS Code里嵌入了其他终端工具,编码处理可能跟原生终端不一样。遇到这种情况,优先在工具自身的配置文件里指定UTF-8,而不是去改系统设置。
4. 系统层面的一次性解决方案
4.1 Windows系统区域设置里的UTF-8开关
如果你不想在每个终端、每个程序里单独设置,Windows 10/11系统层面有一个"大招":打开系统Beta选项,让整个系统默认使用UTF-8编码。
路径是:控制面板 → 区域 → 管理 → 更改系统区域设置 → 勾选"Beta: 使用Unicode UTF-8提供全球语言支持"。
勾选之后重启系统,整个系统的非Unicode程序编码都会切到UTF-8,CMD、记事本、以及大量不指定编码的程序的默认编码都会变成UTF-8。这样一来,VS Code终端里chcp都不用执行了,程序输出中文基本都能正常显示。
但是!这个选项慎开。开启它之后,很多老旧的国产软件、某些依赖GBK编码的软件可能会乱码。我自己就遇到过:某银行的网银插件开启后界面全乱,某老版数据库管理工具日志乱得没法看。这个开关对个人开发者来说很爽,但在公司环境或者有历史软件依赖的情况下,开之前一定评估一下风险。
如果不开系统开关,又想解决全局乱码问题,可以考虑把系统区域格式改成"中文(简体,中国)",并确保不勾选任何奇怪的Beta选项,然后把非Unicode程序的语言设置为"中文(简体,中国)"。这本质上还是让非Unicode程序默认GBK编码,跟UTF-8系统开关方案是两条路线,选一条执行就行,千万别两个都开。
4.2 文件本身的编码问题(VS Code右下角)
有时候终端编译运行程序时报错信息乱码,或者程序输出的乱码跟终端无关,而是源文件本身的编码就不对。这种情况在接手老项目时特别常见:项目文件是GBK编码存的,但VS Code默认按UTF-8打开,导致代码里的中文注释、字符串全部乱掉。
判断方法很简单,看VS Code窗口右下角,那里会显示当前文件的编码。如果是UTF-8,而文件内容里中文显示成了乱码,说明文件实际是GBK编码,VS Code猜错了。
处理办法:点击右下角的编码按钮,选择"Reopen with Encoding",在弹出的列表里选GBK或者GB 18030,文件就能正常显示了。如果想让这个文件永久以GBK编码保存,再点一次编码按钮,选择"Save with Encoding",选GBK。
对于项目级处理,更推荐在settings.json里配置自动猜测编码:
"files.autoGuessEncoding": true开启后VS Code会根据文件内容自动猜测编码,能减少一部分编码判断错误的情况。但它不是万能的,对于纯文本且内容较短的文件,猜测准确率不高。我一般建议:老项目(尤其是带中文注释的C/C++、Java项目),先统一确认编码再动手改代码;新项目全程UTF-8,不要混用。
还有一个相关的配置文件级坑:你的settings.json本身如果是GBK编码保存的,里面写的中文注释、字体名称在VS Code里也可能显示乱码。同样用右下角的编码功能检查一下。
5. 常见问题与排查技巧实录
5.1 排查思路:先定位是显示问题还是输出问题
遇到终端汉字乱码,最快定位问题的方法是做一个"最小复现实验":
第一步,在终端里直接输入一个中文命令提示,比如:
echo 中文测试如果echo输出的中文是正常的,说明终端解码和字体渲染没问题,问题在程序的输出端。如果echo也乱,说明终端本身编码或字体有问题,优先检查终端配置。
第二步,如果确认终端没问题,看程序内部。写一个最简Python脚本:
print("中文测试")设置PYTHONIOENCODING=utf-8跑一遍。如果正常了,说明你的程序里一定有代码在覆盖标准输出的编码,或者你的运行环境变量有异常。如果仍然乱,试着把程序输出重定向到文件:
python script.py > output.txt然后用文本编辑器打开output.txt。如果文件内容正常,说明程序输出的是UTF-8,只是终端解码不对;如果文件内容也是乱码,说明程序输出时就没编对。
这个三步定位法是通用的,不管是Python、C++、Java还是Node.js,都能通过"echo测试→简单脚本测试→重定向观察"把问题缩小到具体环节。我排查编码问题从来不会一上来就翻终端设置,一定是先定位,再动手。
5.2 常见问题速查表
| 现象 | 可能原因 | 优先处理方案 |
|---|---|---|
汉字变成???,数量与字数一致 | 程序输出端编码失败,字符被替换 | 设置PYTHONIOENCODING=utf-8或sys.stdout.reconfigure(encoding='utf-8') |
汉字变成涓枃这类字符 | 程序输出UTF-8,终端用GBK解码 | 执行chcp 65001,或在VS Code终端配置里加入自动切换 |
| 汉字显示为方块/豆腐块 | 字体不支持中文渲染 | 终端字体列表加入微软雅黑或Cascadia Code |
| 源码里中文就乱 | 文件编码与VS Code猜测不一致 | 右下角Reopen with Encoding选择GBK或UTF-8 |
| C/C++编译后输出乱码 | 源码编码、编译选项、运行代码页不一致 | 编译加/utf-8(MSVC)或-fexec-charset=UTF-8,运行前调用SetConsoleOutputCP(CP_UTF8) |
| Java编译产物中文乱码 | Maven/Gradle构建编码与运行编码不一致 | 项目里显式声明UTF-8编码,启动加-Dfile.encoding=UTF-8 |
| 第一次运行正常,换终端后乱码 | 不同终端的默认代码页不同 | 统一终端配置,或在程序端硬性指定UTF-8输出 |
这张表是我日常排查用的速查索引。实际工作中遇到变体问题,先对照现象找到最接近的行,按优先级尝试,绝大多数能快速解决。
5.3 一些实操中踩过的坑
第一个坑是VS Code终端缓存。修改完settings.json里的终端配置后,有时候旧终端标签页仍然带着旧的编码设置。别以为改完配置重启VS Code就万事大吉,必须把终端Tab手动关掉再重新打开。有几次同事跟我说"按你的方法改了没用",我远程一看,他开了个终端就没关过,配置改了根本没生效。
第二个坑是WSL终端的编码跟Windows终端不一样。VS Code里可以同时使用Windows终端和WSL终端,两个的编码逻辑完全不同。WSL内部是Linux环境,默认UTF-8,基本不会乱。但当你通过WSL在Windows文件系统(/mnt/c/)上创建带有中文文件名的文件时,文件名的编码可能会跟Windows侧对上不,导致文件在两边看起来名字不一样。这个跟终端乱码是两个问题,但经常被混在一起排查,如果你的场景涉及文件名乱码,单独查一下挂载选项metadata和utf8是否开启。
第三个坑是Python虚拟环境特殊字符。有段时间我的项目用conda管理环境,conda激活环境时会修改PYTHONIOENCODING或者注入一些脚本输出,导致VS Code终端里中文乱码的规律很奇怪:有时候刚进终端是好的,conda activate之后就开始乱。排查之后发现是conda的初始化脚本里有一段代码把PYTHONIOENCODING设成了GBK相关的值。遇到这种环境管理器导致的编码问题,别在VS Code层面硬调,找一下环境管理器的配置文件,把编码设置统一成UTF-8。
第四个坑是远程开发场景。VS Code Remote-SSH连接到Linux服务器时,终端本身跑在服务器上,编码通常是UTF-8,没问题。但如果你在远程终端里运行的程序反过来输出GBK编码(比如调用了Windows侧的工具链),那就会乱。这种"编码在两端来回横跳"的场景最耗时间,我现在的习惯是:远程开发时,在服务器端的~/.bashrc里强制设置export LC_ALL=C.UTF-8,避免locale不一致引入的干扰。
最后分享一个我自己的小习惯:新项目从第一天开始就全链路UTF-8,源文件UTF-8、构建配置UTF-8、运行时显式指定UTF-8、终端代码页65001。前期多花十分钟设置好,后面省下的是无数个"为什么又乱码了"的下午。编码这种东西,就是个标准问题——只要约定的标准一致,它就不会咬人。