欢迎光临
我们一直在努力

【踩坑记录】UTF-8 和 GBK 编码冲突导致代码全变?Git 为什么没有提示冲突?

前言

最近在做 Memoria 项目的 Flutter / Dart 代码时,遇到了一个非常经典但又很隐蔽的问题:

一个原本正常的 UTF-8 文件,被某个环境用 GBK 打开并保存后,文件内容看起来变成了大量乱码、转义字符,代码 diff 也变得非常恐怖。

更坑的是:

> Git 并不会认为这是“编码冲突”,它只会认为这是一次普通的文件修改。

也就是说,如果一个人把 UTF-8 文件用 GBK 保存了,然后再提交,另一个人 pull 下来之后,Git 很可能不会爆冲突,而是直接把“变了编码后的文件”合并下来。

这篇文章记录一下这个问题的原因、排查方式、修复方式和后续项目规范。

一、问题现象

原本 Dart 文件里有一些中文 prompt、中文注释、语义搜索规则等内容。

结果某次打开后,文件里出现了大量类似下面的内容:

'\\u8bf7\\u6839\\u636e\\u7528\\u6237\\u8f93\\u5165…'

或者直接出现乱码:

璇锋牴鎹甤…

在 IDE 里看起来就是:

  • 中文变成乱码;

  • 字符串被转义成一堆 \\uXXXX;

  • Git diff 显示整个文件大面积变化;

  • 代码本身逻辑没怎么改,但几百行甚至上千行都变了。

这种情况在包含中文 prompt、中文配置、中文注释的项目里尤其容易出现。


二、根本原因:Git 只比较字节流,不理解文本编码

这个问题的核心原因是:

Git 本质上管理的是文件内容的字节流,而不是“人眼看到的文本”。

同一个中文字符,在 UTF-8 和 GBK 里的字节表示是不一样的。

例如中文字符:

在 UTF-8 中是:

E4 B8 AD

在 GBK 中可能是:

D6 D0

人看到的都是“中”,但底层字节完全不同。

所以当一个 UTF-8 文件被 GBK 打开、编辑、保存后,文件的底层字节流可能会发生大面积变化。

Git 看到的是:

旧字节流 != 新字节流

于是它会认为:

这个文件被正常修改了

而不是:

这个文件发生了编码冲突


三、为什么 Git 不会自动报冲突?

很多人会以为:

既然文件被弄乱了,Git 应该提示 conflict 吧?

但实际上,Git 的冲突检测主要发生在三方合并时:

base 版本
你的版本
对方版本

只有当两边修改了同一片内容,并且 Git 无法自动判断该怎么合并时,才会出现:

<<<<<<< HEAD

=======

>>>>>>> branch

但是编码变化通常有两种情况:

情况 1:只有一个人改了编码

比如:

A 提交:正常 UTF-8 文件
B 提交:把文件用 GBK 保存了
C pull:直接拿到 B 的结果

这时候 Git 认为 B 只是正常修改文件,不会产生冲突。

情况 2:两个人改了不同区域

比如:

A 改了上半部分代码
B 用 GBK 保存导致全文件变化

Git 可能仍然会尝试自动合并,不一定冲突。

情况 3:两个人改了同一行

这种情况下有可能冲突,但冲突原因仍然是“同一行被修改”,不是“UTF-8 和 GBK 冲突”。

所以结论是:

Git 没有“编码冲突”这个概念。
它只知道字节变了,不知道你是 UTF-8、GBK、Shift-JIS 还是别的编码。


四、这个问题为什么很危险?

这个坑危险在于,它不像语法错误那样立刻爆炸。

它可能表现为:

  • 代码还能编译;

  • 逻辑似乎没变;

  • Git 没有冲突提示;

  • review 的时候 diff 太大,别人不一定仔细看;

  • 最后乱码文件被正常 merge 进主分支。

  • 尤其是有中文 prompt 的 AI 项目,这个问题会更严重。

    例如:

    final prompt = '''
    你是一个语义查询解析器,请把用户输入解析成 JSON。
    ''';

    如果 prompt 编码坏了,模型收到的提示词可能就变成乱码,后续效果会非常诡异:

    • 搜索结果不稳定;

    • LLM 输出格式异常;

    • 中文查询解析失败;

    • 原本能识别“上个月”“济南”“照片”的逻辑突然失效。

    这类 bug 很难第一时间想到是编码问题。


    五、如何判断是不是编码问题?

    1. 看 Git diff

    如果你发现某个文件明明只改了一点点,但 diff 显示几百行都变了,就要警惕:

    git diff path/to/file.dart

    典型现象:

    – 正常中文
    + \\u6b63\\u5e38\\u4e2d\\u6587

    或者:

    – 请根据用户输入解析
    + 璇锋牴鎹甤…

    2. 看 IDE 右下角编码

    在 VS Code、Android Studio、JetBrains 系 IDE 里,一般右下角会显示当前文件编码:

    UTF-8
    GBK
    GB2312
    UTF-8 with BOM

    如果 Dart / Markdown / JSON 文件不是 UTF-8,就要注意。

    3. 用命令检查编码

    Linux / macOS 可以用:

    file -bi path/to/file.dart

    可能输出:

    text/plain; charset=utf-8

    或者:

    text/plain; charset=iso-8859-1

    Windows 下也可以用 Python 简单检测:

    from pathlib import Path

    path = Path("lib/service/semantic_query_parser_lm.dart")
    data = path.read_bytes()

    try:
    data.decode("utf-8")
    print("UTF-8 OK")
    except UnicodeDecodeError as e:
    print("Not valid UTF-8:", e)


    六、如何修复已经被 GBK 保存的文件?

    情况 1:你不需要保留当前修改

    这是最简单的情况,直接恢复:

    git restore path/to/file.dart

    或者恢复整个工作区:

    git restore .

    注意:这会丢弃本地未提交修改。


    情况 2:你需要保留代码逻辑修改

    如果里面确实有一些有效代码改动,但是编码坏了,可以这样处理:

  • 先备份当前文件;

  • 从 Git 恢复一份正常 UTF-8 版本;

  • 手动把有效代码逻辑迁移过去;

  • 不要直接整文件覆盖;

  • 最后确认 diff 只包含真正的逻辑修改。

  • 建议流程:

    copy lib/service/semantic_query_parser_lm.dart lib/service/semantic_query_parser_lm.dart.bak
    git restore lib/service/semantic_query_parser_lm.dart

    然后打开 .bak,只复制真正需要的代码片段。


    情况 3:只是编码错了,内容还可逆

    如果确定文件是 GBK 编码,可以尝试转回 UTF-8:

    iconv -f gbk -t utf-8 old.dart > new.dart

    或者 Python:

    from pathlib import Path

    src = Path("old.dart")
    dst = Path("new.dart")

    text = src.read_text(encoding="gbk")
    dst.write_text(text, encoding="utf-8")

    但注意:如果文件已经经历过多次错误打开和保存,不一定能完全恢复。


    七、如何防止再次发生?

    1. 统一项目编码:全部使用 UTF-8

    在项目根目录加 .editorconfig:

    root = true

    [*]
    charset = utf-8
    end_of_line = lf
    insert_final_newline = true
    trim_trailing_whitespace = true

    [*.dart]
    charset = utf-8
    end_of_line = lf

    [*.md]
    charset = utf-8
    end_of_line = lf

    [*.json]
    charset = utf-8
    end_of_line = lf

    这样支持 EditorConfig 的 IDE 会自动使用 UTF-8。


    2. VS Code 设置

    项目里可以加:

    {
    "files.encoding": "utf8",
    "files.autoGuessEncoding": false,
    "files.eol": "\\n"
    }

    建议关闭自动猜编码,因为自动猜有时反而会把 UTF-8 文件识别成 GBK。


    3. JetBrains / Android Studio 设置

    路径一般是:

    Settings / Preferences
    -> Editor
    -> File Encodings

    建议设置:

    Global Encoding: UTF-8
    Project Encoding: UTF-8
    Default encoding for properties files: UTF-8

    如果 IDE 提示:

    Reload in another encoding
    Convert to UTF-8

    要优先选择:

    Convert to UTF-8

    而不是随便用 GBK 重新保存。


    4. 添加 Git 提交前检查

    可以写一个简单脚本,阻止非 UTF-8 文件提交。

    例如 scripts/check_utf8.py:

    from pathlib import Path
    import sys

    TEXT_EXTS = {
    ".dart", ".md", ".json", ".yaml", ".yml",
    ".txt", ".py", ".js", ".ts", ".html", ".css"
    }

    failed = []

    for path in Path(".").rglob("*"):
    if not path.is_file():
    continue

    if ".git" in path.parts:
    continue

    if path.suffix.lower() not in TEXT_EXTS:
    continue

    try:
    path.read_text(encoding="utf-8")
    except UnicodeDecodeError:
    failed.append(str(path))

    if failed:
    print("以下文件不是合法 UTF-8:")
    for f in failed:
    print(" -", f)
    sys.exit(1)

    print("UTF-8 check passed.")

    运行:

    python scripts/check_utf8.py

    也可以接到 pre-commit 里。


    5. .gitattributes 统一换行

    .gitattributes 不一定能完全解决编码问题,但可以规范文本文件和换行:

    * text=auto

    *.dart text eol=lf
    *.md text eol=lf
    *.json text eol=lf
    *.yaml text eol=lf
    *.yml text eol=lf
    *.py text eol=lf

    注意:.gitattributes 主要解决文本归一化和换行问题,不能指望它自动帮你识别所有编码错误。


    八、团队协作时怎么说清楚这个问题?

    这类问题最好不要只说:

    你代码乱码了。

    而是应该明确说明:

    这个文件原本是 UTF-8,但被 GBK 或其他编码方式打开并保存了。
    Git 只比较字节流,不理解文本编码,所以它会把这当成普通修改,不一定产生冲突。
    建议恢复原文件后,只迁移真正的逻辑修改,并统一 IDE 编码为 UTF-8。

    这样对方更容易理解问题本质。


    九、这次给我的教训

    这次问题给我的最大提醒是:

    对包含中文 prompt、中文注释、中文配置的 AI 项目来说,编码规范不是小事。

    尤其是像 Memoria 这种项目,里面会有很多中文语义解析 prompt,例如:

    • 时间解析;

    • 地点解析;

    • 相册事件生成;

    • 语义检索;

    • LLM 输出 JSON 模板;

    • 中文标签和规则。

    这些内容一旦被错误编码污染,表面看起来可能只是“中文显示不正常”,但实际上可能影响整个模型调用链路。

    所以后续我会把 UTF-8 编码检查作为项目工程规范的一部分。


    十、总结

    这次问题可以总结成一句话:

    Git 只管理字节流,不理解文本编码。UTF-8 文件被 GBK 打开并保存后,底层字节完全变化,Git 只会认为这是普通修改,不会自动触发所谓“编码冲突”。

    解决方案:

  • 项目统一使用 UTF-8;

  • IDE 全局设置 UTF-8;

  • 不要用 GBK 保存代码文件;

  • 发现 diff 大面积异常时,优先怀疑编码问题;

  • 对已经污染的文件,优先从 Git 恢复,再手动迁移有效逻辑;

  • 添加 .editorconfig、.gitattributes 和 UTF-8 检查脚本;

  • 团队协作时明确说明:这是编码问题,不是普通代码风格问题。

  • 这类 bug 不难修,但如果不重视,很容易悄悄污染主分支。

    所以,尤其是中文 AI 项目,一定要记住:

    代码文件统一 UTF-8。
    Prompt 文件统一 UTF-8。
    配置文件统一 UTF-8。
    不要让 GBK 混进来。

    赞(0)
    未经允许不得转载:171主机测评 » 【踩坑记录】UTF-8 和 GBK 编码冲突导致代码全变?Git 为什么没有提示冲突?
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址