惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
WordPress大学
WordPress大学
U
Unit 42
I
InfoQ
A
About on SuperTechFans
宝玉的分享
宝玉的分享
J
Java Code Geeks
博客园 - 司徒正美
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
G
Google Developers Blog
人人都是产品经理
人人都是产品经理
小众软件
小众软件
Microsoft Security Blog
Microsoft Security Blog
L
LangChain Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
aimingoo的专栏
aimingoo的专栏
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
腾讯CDC
Recent Announcements
Recent Announcements

博客园 - VipSoft

FastAPI 全局 HTTP 异常处理器 + 统一响应封装 SpringBoot 心跳日志不记录 access.log Qdrant Linux 安装(非Docker) LangChain — RAG 知识库(实操) LangChain — RAG 构建知识库(理论) LangChain — RAG 构建知识库(实操) Python PyCharm 运行,取不到 .env 文件中的值 Qdrant 安装(Windows) LangChain — RAG 构建知识库 Python 项目简单部署(Linux) MinerU - 将非结构化文档(PDF、图片、Office 文件等)转换为机器可读的 Markdown 和 JSON LangChain 入门 服务端部署-FastAPI LangChain 入门 LangSmith LangChain 入门 实战 - 食谱推荐 LangChain 入门 Memory 会话记忆 LangChain 入门 Tools 工具 LangChain 入门 Tools 工具 LangChain 入门 Prompts 提示词 LangChain 入门 Message 消息 LangChain 入门 Model 的初始化和调用 LangChain 入门 Agent 的基本运行机制 AI 0基础学习,名词解析 LangChain 和 LangGraph AI大模型知识体系 Dify — Workflow - 数据可视化 Dify — 连接MySQL配置 Dify — Chatflow - 数据库智能查询 Dify — Chatflow - 文档知识库 Dify — Agent 智能体 高安全券码、注册码生成
C# 无BOM的UTF-8编码
VipSoft · 2026-01-14 · via 博客园 - VipSoft

你发现了一个很常见的编码问题。System.Text.Encoding.GetEncoding("utf-8") 默认情况下会使用带有BOM(Byte Order Mark,字节顺序标记)的UTF-8编码。BOM在某些场景下会导致问题,比如命令行显示乱码。

问题分析

  1. UTF-8 vs UTF-8-BOM:

    • UTF-8: 纯UTF-8,不包含BOM
    • UTF-8-BOM: UTF-8 with BOM,在文件开头有EF BB BF三个字节作为标记
  2. 命令行显示乱码的原因:

    • Windows命令行(cmd)默认使用GBK编码
    • 当UTF-8-BOM文件在命令行显示时,BOM字符会被显示为乱码"锘"
    • 后续内容也可能因为编码不匹配而显示乱码

解决方案

以下是修改后的WriteLog方法,解决编码问题:

方案1:使用无BOM的UTF-8编码(推荐)

private void WriteLog(string fileName, string msg)
{
    System.IO.FileStream fst = null;
    System.IO.StreamWriter swt = null;
    try
    {
        string time = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        string savePath = ConfigurationManager.AppSettings["FileTempPath"];
        string filePath = Path.Combine(savePath, fileName);
        
        // 确保目录存在
        string directory = Path.GetDirectoryName(filePath);
        if (!Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
        }
        
        // 使用无BOM的UTF-8编码
        var utf8WithoutBom = new System.Text.UTF8Encoding(false);
        
        // 方法1:使用StreamWriter构造函数
        fst = new System.IO.FileStream(filePath, System.IO.FileMode.Append);
        swt = new System.IO.StreamWriter(fst, utf8WithoutBom);
        
        // 方法2:或者使用File.AppendText(更简洁)
        // using (swt = System.IO.File.AppendText(filePath))
        // {
        //     swt.WriteLine(time + " | " + msg.ToString());
        // }
        
        swt.WriteLine(time + " | " + msg);
    }
    catch (Exception ex)
    {
        logger.Error(ex, ex.Message);
    }
    finally
    {
        if (swt != null)
        {
            swt.Close();
        }
        if (fst != null)
        {
            fst.Close();
        }
    }
}

方案2:使用File.AppendText(自动处理编码)

private void WriteLog(string fileName, string msg)
{
    try
    {
        string time = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        string savePath = ConfigurationManager.AppSettings["FileTempPath"];
        string filePath = Path.Combine(savePath, fileName);
        
        // 确保目录存在
        string directory = Path.GetDirectoryName(filePath);
        if (!Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
        }
        
        // 使用File.AppendText(默认无BOM的UTF-8)
        using (var sw = System.IO.File.AppendText(filePath))
        {
            sw.WriteLine(time + " | " + msg);
        }
    }
    catch (Exception ex)
    {
        logger.Error(ex, ex.Message);
    }
}

方案3:更完整的版本(支持编码选择)

private void WriteLog(string fileName, string msg, bool useUtf8Bom = false)
{
    try
    {
        string time = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        string savePath = ConfigurationManager.AppSettings["FileTempPath"];
        string filePath = Path.Combine(savePath, fileName);
        
        // 确保目录存在
        string directory = Path.GetDirectoryName(filePath);
        if (!Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
        }
        
        string content = time + " | " + msg + Environment.NewLine;
        
        // 根据参数选择编码
        System.Text.Encoding encoding;
        if (useUtf8Bom)
        {
            encoding = System.Text.Encoding.UTF8; // 带BOM
        }
        else
        {
            encoding = new System.Text.UTF8Encoding(false); // 不带BOM
        }
        
        // 追加写入文件
        System.IO.File.AppendAllText(filePath, content, encoding);
    }
    catch (Exception ex)
    {
        logger.Error(ex, ex.Message);
    }
}

关键修改说明

  1. 使用new UTF8Encoding(false):

    // false 表示不包含BOM
    var utf8WithoutBom = new System.Text.UTF8Encoding(false);
    
  2. 使用File.AppendText:

    • 这个方法默认使用无BOM的UTF-8编码
    • 代码更简洁,自动处理资源释放
  3. 使用File.AppendAllText:

    • 一次性完成写入操作
    • 可以指定编码格式
  4. 改进路径处理:

    • 使用Path.Combine代替字符串拼接
    • 使用Path.GetDirectoryName获取目录路径

测试验证

你可以创建一个测试方法来验证编码:

private void TestEncoding()
{
    string testFile = "test_log.txt";
    string testMsg = "测试中文内容";
    
    // 写入测试日志
    WriteLog(testFile, testMsg);
    
    // 读取并检查文件编码
    string filePath = Path.Combine(
        ConfigurationManager.AppSettings["FileTempPath"], 
        testFile);
    
    if (File.Exists(filePath))
    {
        byte[] bytes = File.ReadAllBytes(filePath);
        if (bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF)
        {
            Console.WriteLine("文件包含BOM(UTF-8-BOM)");
        }
        else
        {
            Console.WriteLine("文件不包含BOM(纯UTF-8)");
        }
    }
}

对于命令行的建议

如果需要在命令行正确显示UTF-8文件,可以:

  1. 修改命令行编码:

    chcp 65001
    

    这会将命令行编码改为UTF-8

  2. 使用支持UTF-8的终端:

    • Windows Terminal
    • PowerShell Core
    • Git Bash
  3. 在代码中显式指定编码(如果需要在命令行显示):

    // 如果需要兼容命令行显示,可以使用GBK编码
    var gbkEncoding = System.Text.Encoding.GetEncoding("GBK");
    using (var sw = new StreamWriter(filePath, true, gbkEncoding))
    {
        sw.WriteLine(time + " | " + msg);
    }
    

建议使用方案2File.AppendText),因为它最简单且自动处理了无BOM的UTF-8编码问题。