You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Stream.Write写入单行文生成UTF-16LE多行文为UTF8问题咨询

问题背景

我们有两个关联的处理流程:

  • 流程1:读取存储目录列表的文本文件,将所有行转换为字节数组后保存为dat文件,文件格式为[文件大小][行数][目录列表],示例格式为[12 0 0 0][1 0 0 0][92 0 97 0 ....]
  • 流程2:调用REST API流式传输该dat文件,再将其转换回文本文件
异常现象

当原文本文件只有1行时,转换流程生成的文本文件编码为UTF-16 LE;当文件行数大于1时,生成的文本文件编码为UTF-8。

相关代码

完整转换逻辑:

public static void ExtractDatFileData(string datFilePath)
{
    // Open DAT file as stream
    var info = new FileInfo(datFilePath);
    var stream = info.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

    // Write stream into "data.dat" file (creates a copy to read that data from it)
    var inputFile = Path.Combine(@"C:\demo\data.dat");
    using (var file = File.OpenWrite(inputFile))
    {
        stream.CopyTo(file);
    }

    const int BUFFER_SIZE = 1024 * 1024;
    byte[] buffer = new byte[BUFFER_SIZE];

    using (Stream input = File.OpenRead(inputFile))
    {
        // Read the size of the file
        int bytesRead = input.Read(buffer, 0, 4);
        int fileSize = BitConverter.ToInt32(buffer, 0);

        // Read data from the DAT file
        while (input.Position < input.Length)
        {
            // --- directories list ---
            var dirTxtPath = Path.Combine(@"C:\demo\directories.txt");
            using (Stream output = File.Create(dirTxtPath))
            {
                int remaining = fileSize;
                while (remaining > 0 && (bytesRead = input.Read(buffer, 0, Math.Min(remaining, BUFFER_SIZE))) > 0)
                {
                    output.Write(buffer, 0, bytesRead);
                    remaining -= bytesRead;
                }
            }
        }
    }
}

初步定位的问题代码段:

var dirTxtPath = Path.Combine(@"C:\demo\directories.txt");
using (Stream output = File.Create(dirTxtPath))
{
    int remaining = fileSize;
    while (remaining > 0 && (bytesRead = input.Read(buffer, 0, Math.Min(remaining, BUFFER_SIZE))) > 0)
    {
        output.Write(buffer, 0, bytesRead);
        remaining -= bytesRead;
    }
}

看起来output.Write(buffer, 0, bytesRead);在不同场景下会分别以UTF-16或UTF-8写入,但我们不清楚它的文件编码判定逻辑。

已完成排查
  • 确认原始dat文件的写入逻辑对单行和多行文件完全一致,多次核验无误。
  • 使用Beyond Compare对比不同场景生成的dat文件,确认数据格式完全相同。
问题根因分析

首先纠正一个认知误区:File.Create返回的是纯字节流FileStream,Write方法本身不会做任何编码转换,完全按照输入的字节原样写入磁盘,不存在自动判定编码的逻辑。你观察到的编码差异本质是文本编辑器自动检测编码的误判,不是代码写入时用了不同编码。

导致误判的核心原因是代码存在格式解析错误:
你的dat文件规范是[4字节文件大小][4字节行数][N字节目录内容],但代码只读取了前4字节的文件大小,没有跳过接下来的4字节行数字段,直接把行数字段+目录内容全部写入到了txt文件中,额外写入的4字节行数字段干扰了编辑器的编码检测逻辑:

  1. 当原文件只有1行时,行数字段的小端编码为01 00 00 00,加上后续目录内容的字节刚好全部符合「偶数个字节、每两个字节的第二个字节为0」的UTF-16 LE特征,因此编辑器自动判定为UTF-16 LE编码。
  2. 当原文件行数大于1时,目录内容中存在多个换行符,对应的字节不符合UTF-16 LE的双字节编码规则,编辑器就会自动判定为UTF-8编码。

你可以用十六进制编辑器直接打开生成的directories.txt验证:单行和多行场景下的字节写入逻辑完全一致,差异仅为文本编辑器的自动识别结果。

修复方案

读取完4字节的文件大小后,先跳过4字节的行数字段(或者按规范读取行数做校验),再读取后续的目录内容写入文本文件即可解决问题。

内容的提问来源于stack exchange,提问作者Dardar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 05:57:00