Windows 上彻底解决 Codex / Claude / OpenCode 中文乱码

乱码不修,AI 会把你的源码写坏。这篇给出诊断+修复+全自动部署脚本,支持按需选择安装哪个工具。


一、先判断:是显示乱码,还是文件被写坏了?

打开 AI 工具跑几个命令,如果看到:

中文 → ?????、äã、â–、锟斤拷、测试
emoji → 方框、问号

首先要区分两种情况:

类型 表现 严重程度
终端显示乱码 只在终端输出里乱码,源码文件本身正常 烦人但不危险
文件内容损坏 VS Code 打开源码也乱码,git diff 看到 AI 改了文件 必须立即处理

重点原则:不要让 AI 看到终端乱码后去"修复"源文件。 它会把正常的中文替换成真的乱码字节。


二、为什么 Windows 上 AI CLI 会乱码?

Windows 中文版默认使用 CP936 (GBK) 编码,而 AI 工具内部调用的 PowerShell / Bash 输出的中文是 UTF-8。这两者对不上,就成了乱码。

关键链路:

AI 调用 PowerShell
  → powershell.exe -NoProfile(跳过了你的 UTF-8 设置)
    → Console OutputEncoding = gb2312
      → UTF-8 字节被按 GBK 解码
        → 乱码传给 AI

三个工具的共同问题:

  1. Windows PowerShell 5.1 默认 code page 是 936(中文系统)
  2. AI 工具常用 -NoProfile 启动 PowerShell,绕过了用户的 profile 设置
  3. Console OutputEncoding 不是 UTF-8,管道输出被错误编码
  4. 字体不含 CJK(中文/日文/韩文)字符

三、快速诊断(30 秒)

在 PowerShell 中运行:

# 查看当前编码状态
cmd /c chcp

# 查看 PowerShell 编码设置
[Console]::InputEncoding.WebName
[Console]::OutputEncoding.WebName
$OutputEncoding.WebName

# Unicode 烟雾测试
"中文测试 😀 äöü Привет こんにちは 한국어"

期望看到:

Active code page: 65001      # ← 必须是 65001
ConsoleInputEncoding  = utf-8
ConsoleOutputEncoding = utf-8
OutputEncoding        = utf-8
中文测试 😀 äöü Привет こんにちは 한국어   # ← 完整显示

如果你的 code page 是 936,OutputEncoding 是 gb2312,中文显示成 ??? → 继续往下看。


四、一键修复脚本(支持按需安装)

下面的脚本会:

  1. 安装 PowerShell 7(如果还没装)
  2. 创建 UTF-8 启动 wrapper(codexu / claudeu / opencodeu
  3. 给 PowerShell profile 添加 UTF-8 引导
  4. 配置 Git UTF-8 参数
  5. 为每个已安装的 AI 工具添加编码规则

完整部署脚本

把下面脚本保存为 setup-ai-cli-utf8.ps1,以管理员身份运行:

# setup-ai-cli-utf8.ps1
# Windows AI CLI 乱码修复一键脚本
# 支持 Codex CLI / Claude Code / OpenCode
# 用法: powershell -ExecutionPolicy Bypass -File .\setup-ai-cli-utf8.ps1

param(
    [switch]$Codex,      # 只配置 Codex CLI
    [switch]$Claude,     # 只配置 Claude Code
    [switch]$OpenCode,   # 只配置 OpenCode
    [switch]$All         # 配置所有已安装的工具(默认)
)

$ErrorActionPreference = "Stop"

# 如果没有指定,默认处理所有已安装的工具
if (-not ($Codex -or $Claude -or $OpenCode)) {
    $All = $true
}

Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Windows AI CLI UTF-8 修复脚本" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

# === 诊断当前状态 ===
Write-Host "`n=== 诊断当前编码状态 ===" -ForegroundColor Yellow

$CurrentCodePage = cmd /c chcp 2>&1
Write-Host "当前 Code Page: $CurrentCodePage"

$ConsoleOutEnc = [Console]::OutputEncoding.WebName
$OutputEnc = $OutputEncoding.WebName
Write-Host "Console OutputEncoding: $ConsoleOutEnc"
Write-Host "OutputEncoding: $OutputEnc"

if ($ConsoleOutEnc -eq "utf-8" -and $OutputEnc -eq "utf-8") {
    Write-Host "编码已为 UTF-8,无需修复。" -ForegroundColor Green
    Write-Host "如果仍有乱码,可能是字体问题,跳转到字体检查部分。"
    $NeedsFix = $false
} else {
    Write-Host "编码不是 UTF-8,需要修复。" -ForegroundColor Red
    $NeedsFix = $true
}

# === [1/6] 安装 PowerShell 7 ===
Write-Host "`n=== [1/6] 检查并安装 PowerShell 7 ===" -ForegroundColor Yellow

$HasPwsh = Get-Command pwsh -ErrorAction SilentlyContinue
if ($HasPwsh) {
    Write-Host "PowerShell 7 已安装: $(pwsh -Version)" -ForegroundColor Green
} else {
    Write-Host "正在通过 winget 安装 PowerShell 7..." -ForegroundColor Yellow
    try {
        winget install --id Microsoft.PowerShell --source winget --accept-source-agreements --accept-package-agreements
        Write-Host "PowerShell 7 安装完成" -ForegroundColor Green
    } catch {
        Write-Host "winget 安装失败,请手动安装: https://github.com/PowerShell/PowerShell" -ForegroundColor Red
    }
}

# === [2/6] 创建 UTF-8 Core 脚本 ===
Write-Host "`n=== [2/6] 创建 UTF-8 引导脚本 ===" -ForegroundColor Yellow

$BinDir = Join-Path $env:USERPROFILE "bin"
New-Item -ItemType Directory -Path $BinDir -Force | Out-Null

$CoreScript = Join-Path $BinDir "ai-cli-utf8-core.ps1"

$CoreContent = @'
# ai-cli-utf8-core.ps1
# 所有 AI CLI wrapper 共用的 UTF-8 引导脚本

try {
    chcp 65001 | Out-Null
} catch {
    # chcp 不可用时忽略
}

$Utf8NoBom = [System.Text.UTF8Encoding]::new($false)

try { [Console]::InputEncoding  = $Utf8NoBom } catch {}
try { [Console]::OutputEncoding = $Utf8NoBom } catch {}

$global:OutputEncoding = $Utf8NoBom

# 语言运行时编码
$env:PYTHONUTF8       = "1"
$env:PYTHONIOENCODING = "utf-8"
$env:LESSCHARSET      = "utf-8"

# Git Bash / MSYS 兼容
if (-not $env:LANG) {
    $env:LANG = "C.UTF-8"
}
'@

Set-Content -Path $CoreScript -Value $CoreContent -Encoding UTF8
Write-Host "核心引导脚本已创建: $CoreScript" -ForegroundColor Green

# === [3/6] 创建 Wrapper 脚本 ===
Write-Host "`n=== [3/6] 创建 UTF-8 Wrapper ===" -ForegroundColor Yellow

function New-Wrapper {
    param([string]$ToolName)
    
    $Ps1Path = Join-Path $BinDir "$($ToolName)u.ps1"
    $CmdPath = Join-Path $BinDir "$($ToolName)u.cmd"
    
    # .ps1 wrapper
    $Ps1Content = @"
param(
  [Parameter(ValueFromRemainingArguments = `$true)]
  [string[]]`$RemainingArgs
)

. "`$PSScriptRoot\ai-cli-utf8-core.ps1"

`$cmd = Get-Command "$ToolName" -ErrorAction SilentlyContinue
if (-not `$cmd) {
  Write-Error "Command '$ToolName' was not found in PATH. Install it first, then retry."
  exit 127
}

& "$ToolName" @RemainingArgs
exit `$LASTEXITCODE
"@
    
    Set-Content -Path $Ps1Path -Value $Ps1Content -Encoding UTF8
    
    # .cmd wrapper (兼容直接在 cmd 中调用)
    $CmdContent = @"
@echo off
chcp 65001 >nul
where pwsh >nul 2>nul
if errorlevel 1 (
  powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%~dp0$($ToolName)u.ps1" %*
) else (
  pwsh -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%~dp0$($ToolName)u.ps1" %*
)
"@
    
    Set-Content -Path $CmdPath -Value $CmdContent -Encoding ASCII
    
    Write-Host "  $($ToolName)u.ps1 / $($ToolName)u.cmd 已创建" -ForegroundColor Green
}

# 检查哪些工具已安装
$HasCodex = Get-Command codex -ErrorAction SilentlyContinue
$HasClaude = Get-Command claude -ErrorAction SilentlyContinue
$HasOpenCode = Get-Command opencode -ErrorAction SilentlyContinue

if ($Codex -or $All) {
    if ($HasCodex) {
        New-Wrapper "codex"
    } else {
        Write-Host "  Codex CLI 未安装,跳过" -ForegroundColor DarkYellow
    }
}

if ($Claude -or $All) {
    if ($HasClaude) {
        New-Wrapper "claude"
    } else {
        Write-Host "  Claude Code 未安装,跳过" -ForegroundColor DarkYellow
    }
}

if ($OpenCode -or $All) {
    if ($HasOpenCode) {
        New-Wrapper "opencode"
    } else {
        Write-Host "  OpenCode 未安装,跳过" -ForegroundColor DarkYellow
    }
}

# === [4/6] 配置 PATH ===
Write-Host "`n=== [4/6] 配置系统 PATH ===" -ForegroundColor Yellow

$UserPath = [Environment]::GetEnvironmentVariable("Path", "User")
if ($UserPath -notlike "*$BinDir*") {
    $NewPath = if ($UserPath) { "$UserPath;$BinDir" } else { $BinDir }
    [Environment]::SetEnvironmentVariable("Path", $NewPath, "User")
    $env:Path = "$env:Path;$BinDir"
    Write-Host "已将 $BinDir 添加到用户 PATH" -ForegroundColor Green
} else {
    Write-Host "$BinDir 已在 PATH 中" -ForegroundColor DarkYellow
}

# === [5/6] 配置 PowerShell Profile ===
Write-Host "`n=== [5/6] 配置 PowerShell Profile ===" -ForegroundColor Yellow

$ProfileDir = Split-Path $PROFILE -Parent
New-Item -ItemType Directory -Path $ProfileDir -Force | Out-Null

$Marker = "# >>> AI CLI UTF-8 bootstrap >>>"
$ProfileNeedsPatch = $true
if (Test-Path $PROFILE) {
    $ProfileNeedsPatch = -not (Select-String -Path $PROFILE -SimpleMatch $Marker -Quiet)
}

if ($ProfileNeedsPatch) {
    $Snippet = @"

# >>> AI CLI UTF-8 bootstrap >>>
`$AiCliUtf8Core = Join-Path `$env:USERPROFILE 'bin\ai-cli-utf8-core.ps1'
if (Test-Path `$AiCliUtf8Core) {
  . `$AiCliUtf8Core
}
# <<< AI CLI UTF-8 bootstrap <<<
"@
    Add-Content -Path $PROFILE -Value $Snippet -Encoding UTF8
    Write-Host "PowerShell Profile 已添加 UTF-8 引导" -ForegroundColor Green
} else {
    Write-Host "Profile 中已存在 UTF-8 引导" -ForegroundColor DarkYellow
}

# 同样为 PowerShell 7 配置 profile
$PwshProfile = Join-Path ([Environment]::GetFolderPath('MyDocuments')) "PowerShell\Microsoft.PowerShell_profile.ps1"
$PwshProfileDir = Split-Path $PwshProfile -Parent
New-Item -ItemType Directory -Path $PwshProfileDir -Force | Out-Null

$PwshNeedsPatch = $true
if (Test-Path $PwshProfile) {
    $PwshNeedsPatch = -not (Select-String -Path $PwshProfile -SimpleMatch $Marker -Quiet)
}

if ($PwshNeedsPatch) {
    Add-Content -Path $PwshProfile -Value $Snippet -Encoding UTF8
    Write-Host "PowerShell 7 Profile 已添加 UTF-8 引导" -ForegroundColor Green
}

# === [6/6] 配置 Git & 工具规则 ===
Write-Host "`n=== [6/6] 配置 Git UTF-8 和工具编码规则 ===" -ForegroundColor Yellow

# Git
try {
    git config --global core.quotepath false
    git config --global i18n.logOutputEncoding utf-8
    git config --global i18n.commitEncoding utf-8
    Write-Host "Git UTF-8 配置完成" -ForegroundColor Green
} catch {
    Write-Host "Git 配置失败(可能未安装),跳过" -ForegroundColor DarkYellow
}

# 编码规则内容
$EncodingRules = @"

# Windows encoding rules

- When running PowerShell on Windows, set UTF-8 first:
  `chcp 65001; [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(`$false); `$OutputEncoding = [System.Text.UTF8Encoding]::new(`$false)`.
- Do not judge source file corruption from terminal-rendered mojibake alone.
- Before rewriting files that contain non-ASCII text, validate bytes using strict UTF-8 decoding.
- Preserve existing file encoding where possible.
- Prefer UTF-8 without BOM for cross-platform source files.
- For `.ps1` scripts intended for Windows PowerShell 5.1 with non-ASCII characters, UTF-8 with BOM may be safer.
"@

# Codex CLI: 写入 AGENTS.md
if ($Codex -or $All) {
    if ($HasCodex) {
        $CodexDir = Join-Path $env:USERPROFILE ".codex"
        if (Test-Path $CodexDir) {
            $CodexAgents = Join-Path $CodexDir "AGENTS.md"
            $Existing = if (Test-Path $CodexAgents) { Get-Content $CodexAgents -Raw } else { "" }
            if ($Existing -notmatch "Windows encoding rules") {
                Set-Content -Path $CodexAgents -Value "$Existing$EncodingRules" -Encoding UTF8
                Write-Host "Codex CLI AGENTS.md 已添加编码规则" -ForegroundColor Green
            } else {
                Write-Host "Codex CLI AGENTS.md 已包含编码规则" -ForegroundColor DarkYellow
            }
        }
    }
}

# Claude Code: 写入 CLAUDE.md
if ($Claude -or $All) {
    if ($HasClaude) {
        $ClaudeDir = Join-Path $env:USERPROFILE ".claude"
        if (Test-Path $ClaudeDir) {
            $ClaudeMd = Join-Path $ClaudeDir "CLAUDE.md"
            $Existing = if (Test-Path $ClaudeMd) { Get-Content $ClaudeMd -Raw } else { "" }
            if ($Existing -notmatch "Windows encoding rules") {
                Set-Content -Path $ClaudeMd -Value "$Existing$EncodingRules" -Encoding UTF8
                Write-Host "Claude Code CLAUDE.md 已添加编码规则" -ForegroundColor Green
            } else {
                Write-Host "Claude Code CLAUDE.md 已包含编码规则" -ForegroundColor DarkYellow
            }
        }
    }
}

# OpenCode: 写入 AGENTS.md
if ($OpenCode -or $All) {
    if ($HasOpenCode) {
        $OpenCodeDir = Join-Path $env:USERPROFILE ".opencode"
        if (-not (Test-Path $OpenCodeDir)) {
            New-Item -ItemType Directory -Path $OpenCodeDir -Force | Out-Null
        }
        $OpenCodeAgents = Join-Path $OpenCodeDir "AGENTS.md"
        $Existing = if (Test-Path $OpenCodeAgents) { Get-Content $OpenCodeAgents -Raw } else { "" }
        if ($Existing -notmatch "Windows encoding rules") {
            Set-Content -Path $OpenCodeAgents -Value "$Existing$EncodingRules" -Encoding UTF8
            Write-Host "OpenCode AGENTS.md 已添加编码规则" -ForegroundColor Green
        } else {
            Write-Host "OpenCode AGENTS.md 已包含编码规则" -ForegroundColor DarkYellow
        }
    }
}

# === 完成 ===
Write-Host "`n========================================" -ForegroundColor Green
Write-Host " UTF-8 修复完成!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host "已创建的 Wrapper 命令:" -ForegroundColor Cyan

if ($HasCodex) { Write-Host "  codexu     -> 启动 Codex CLI (UTF-8)" -ForegroundColor White }
if ($HasClaude) { Write-Host "  claudeu    -> 启动 Claude Code (UTF-8)" -ForegroundColor White }
if ($HasOpenCode) { Write-Host "  opencodeu  -> 启动 OpenCode (UTF-8)" -ForegroundColor White }

Write-Host ""
Write-Host "使用方式:" -ForegroundColor Cyan
Write-Host "  # 新开一个 PowerShell 窗口,运行:"
Write-Host ""
if ($HasCodex) { Write-Host "  codexu" -ForegroundColor White }
if ($HasClaude) { Write-Host "  claudeu" -ForegroundColor White }
if ($HasOpenCode) { Write-Host "  opencodeu" -ForegroundColor White }

Write-Host ""
Write-Host "验证 UTF-8 是否生效:" -ForegroundColor Cyan
Write-Host "  pwsh -Command `"chcp; Write-Host '中文测试 😀'`""

Write-Host ""
Write-Host "如果仍然乱码,检查:" -ForegroundColor Yellow
Write-Host "  1. 终端字体是否支持 CJK(推荐 Windows Terminal + Sarasa Gothic / Cascadia Code)"
Write-Host "  2. 是否用 codexu/claudeu/opencodeu 启动(而非 codex/claude/opencode)"
Write-Host "  3. 项目是否在 WSL2 中运行效果更好"

按需安装示例

# 安装所有已检测到的工具
powershell -ExecutionPolicy Bypass -File .\setup-ai-cli-utf8.ps1

# 只配置 Codex CLI
powershell -ExecutionPolicy Bypass -File .\setup-ai-cli-utf8.ps1 -Codex

# 只配置 Claude Code
powershell -ExecutionPolicy Bypass -File .\setup-ai-cli-utf8.ps1 -Claude

# 只配置 OpenCode
powershell -ExecutionPolicy Bypass -File .\setup-ai-cli-utf8.ps1 -OpenCode

五、工具专项处理

5.1 Codex CLI 额外配置

Codex CLI 有一个实验性的 powershell_utf8 feature。检查是否可用:

codex features list

如果列表中有 powershell_utf8,启用它:

codex features enable powershell_utf8

如果报 unknown feature,说明你的版本还不支持——没关系,用 codexu wrapper 就够了。

也可以直接编辑配置文件 ~\.codex\config.toml

[features]
powershell_utf8 = true

5.2 Claude Code 额外配置

Claude Code 可以使用 Git Bash 而不是 PowerShell。如果你的 Git Bash 路径不对,在 ~\.claude\settings.json 中指定:

{
  "env": {
    "CLAUDE_CODE_GIT_BASH_PATH": "C:\\Program Files\\Git\\bin\\bash.exe"
  }
}

如果想强制使用 PowerShell:

{
  "env": {
    "CLAUDE_CODE_USE_POWERSHELL_TOOL": "1"
  },
  "defaultShell": "powershell"
}

如果 PowerShell tool 导致乱码而 Git Bash 正常:

{
  "env": {
    "CLAUDE_CODE_USE_POWERSHELL_TOOL": "0"
  }
}

5.3 OpenCode 额外处理

OpenCode 官方推荐 WSL。如果 native Windows 一直乱码:

# 安装 WSL2
wsl --install

# 在 WSL 内
curl -fsSL https://opencode.ai/install | bash
opencode

如果坚持 Windows native,务必用 opencodeu 启动。


六、字体:最后一道防线

编码已经是 UTF-8 了,但中文还是方框?换字体

Windows Terminal 推荐字体:

在 Windows Terminal 中:设置 → 配置文件 → PowerShell → 外观 → 字体

VS Code 集成终端字体:

{
  "terminal.integrated.fontFamily": "'Cascadia Code PL', 'Sarasa Mono SC', 'Fira Code'"
}

七、严格 UTF-8 文件校验

如果怀疑某个源文件已经被乱码损坏,用这个函数检查(而非肉眼判断终端输出):

function Test-StrictUtf8File {
    param([Parameter(Mandatory = $true)][string]$Path)

    $Utf8Strict = [System.Text.UTF8Encoding]::new($false, $true)
    try {
        $Bytes = [System.IO.File]::ReadAllBytes((Resolve-Path $Path))
        $null = $Utf8Strict.GetString($Bytes)
        [pscustomobject]@{
            Path       = $Path
            StrictUtf8 = $true
            Length     = $Bytes.Length
            Error      = $null
        }
    } catch {
        [pscustomobject]@{
            Path       = $Path
            StrictUtf8 = $false
            Length     = $null
            Error      = $_.Exception.Message
        }
    }
}

# 批量检查源码文件
Get-ChildItem -Recurse -File | Where-Object {
    $_.Extension -in ".md",".txt",".json",".js",".ts",".tsx",".py",".ps1",".rs",".go",".toml"
} | ForEach-Object { Test-StrictUtf8File $_.FullName } | Where-Object { -not $_.StrictUtf8 }

八、安全写文件

在 Windows 上写含中文的文件时,不要用:

# 错误——在 PS5.1 中会产生非 UTF-8 输出
"中文内容" > file.txt
"中文内容" | Out-File file.txt

正确做法:

# PowerShell 7
Set-Content file.txt "中文内容 😀" -Encoding utf8NoBOM

# PowerShell 5.1(.NET API 写 UTF-8 without BOM)
$text = "中文内容 😀"
[System.IO.File]::WriteAllText("file.txt", $text, [System.Text.UTF8Encoding]::new($false))

九、执行顺序总结

1. [ ] 运行诊断 → 确认 code page 是 936
2. [ ] 安装 PowerShell 7 → winget install Microsoft.PowerShell
3. [ ] 运行 setup-ai-cli-utf8.ps1 → 自动创建 wrapper + profile + 规则
4. [ ] 新开终端 → codexu / claudeu / opencodeu 启动
5. [ ] 验证 → "中文测试 😀" 完整显示
6. [ ] 换字体 → 如果仍有方框
7. [ ] 检查 Codex features → powershell_utf8
8. [ ] 配置 Claude shell → settings.json
9. [ ] 严格校验文件 → Test-StrictUtf8File
10.[ ] 仍不行 → 迁移到 WSL2

十、WSL2:终极方案

如果以上都试了还是乱码(尤其是 OpenCode),把项目迁移到 WSL2:

# 安装 WSL
wsl --install

# 进入 WSL
wsl

# 在 WSL 内安装工具
npm i -g @openai/codex          # Codex CLI
curl -fsSL https://claude.ai/install.sh | bash  # Claude Code
curl -fsSL https://opencode.ai/install | bash    # OpenCode

WSL 内的 Linux 原生环境天然就是 UTF-8,不存在编码问题。


总结

Windows 中文 AI CLI 乱码三件套:

  1. PowerShell 7 替代 PS 5.1
  2. codexu / claudeu / opencodeu wrapper 启动
  3. AGENTS.md / CLAUDE.md 编码规则,防止 AI 误判

装好就忘,AI 再也不会把你的中文源码写坏了。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐