C# 无BOM的UTF-8编码
你发现了一个很常见的编码问题。System.Text.Encoding.GetEncoding("utf-8") 默认情况下会使用带有BOM(Byte Order Mark,字节顺序标记)的UTF-8编码。BOM在某些场景下会导致问题,比如命令行显示乱码。
问题分析
-
UTF-8 vs UTF-8-BOM:
- UTF-8: 纯UTF-8,不包含BOM
- UTF-8-BOM: UTF-8 with BOM,在文件开头有
EF BB BF三个字节作为标记
-
命令行显示乱码的原因:
- 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);
}
}
关键修改说明
-
使用
new UTF8Encoding(false):// false 表示不包含BOM var utf8WithoutBom = new System.Text.UTF8Encoding(false); -
使用
File.AppendText:- 这个方法默认使用无BOM的UTF-8编码
- 代码更简洁,自动处理资源释放
-
使用
File.AppendAllText:- 一次性完成写入操作
- 可以指定编码格式
-
改进路径处理:
- 使用
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文件,可以:
-
修改命令行编码:
chcp 65001这会将命令行编码改为UTF-8
-
使用支持UTF-8的终端:
- Windows Terminal
- PowerShell Core
- Git Bash
-
在代码中显式指定编码(如果需要在命令行显示):
// 如果需要兼容命令行显示,可以使用GBK编码 var gbkEncoding = System.Text.Encoding.GetEncoding("GBK"); using (var sw = new StreamWriter(filePath, true, gbkEncoding)) { sw.WriteLine(time + " | " + msg); }
建议使用方案2(File.AppendText),因为它最简单且自动处理了无BOM的UTF-8编码问题。
本文来自博客园,作者:VipSoft 转载请注明原文链接:https://www.cnblogs.com/vipsoft/p/19480838
浙公网安备 33010602011771号