C# 无BOM的UTF-8编码

你发现了一个很常见的编码问题。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编码问题。

posted @ 2026-01-14 10:47  VipSoft  阅读(6)  评论(0)    收藏  举报