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

C#如何以最小文件体积保存byte数组集合?

如何最小化固定长度字节数组集合的序列化体积

兄弟,你现在把每个byte转成两位ASCII字符的做法,相当于每个实际字节占用了2字节的存储空间,这就是文件体积过大的核心原因。咱们可以从几个层面优化,一步步把体积压到最小:

基础优化:直接写入原始二进制数据

这是最容易实现、效果也很明显的优化——别转字符串了,直接把字节数组以二进制形式写入文件,每个byte只占1字节,直接砍掉一半体积。

写入示例代码

using System.IO;

public static void WritePatternsToBinaryFile(byte[][] patterns, string filePath)
{
    // 先写入pattern的固定长度和总数量,反序列化时能准确拆分
    int patternLength = patterns[0].Length;
    using (var writer = new BinaryWriter(File.Open(filePath, FileMode.Create)))
    {
        writer.Write(patternLength);
        writer.Write(patterns.Length);
        
        // 逐个写入每个pattern的原始字节
        foreach (var pattern in patterns)
        {
            writer.Write(pattern);
        }
    }
}

读取示例代码

using System.IO;

public static byte[][] ReadPatternsFromBinaryFile(string filePath)
{
    using (var reader = new BinaryReader(File.Open(filePath, FileMode.Open)))
    {
        int patternLength = reader.ReadInt32();
        int patternCount = reader.ReadInt32();
        
        var patterns = new byte[patternCount][];
        for (int i = 0; i < patternCount; i++)
        {
            // 按固定长度读取每个pattern的字节
            patterns[i] = reader.ReadBytes(patternLength);
        }
        
        return patterns;
    }
}

用这种方式,50000个长度为12的pattern,体积是50000 * 12 = 600,000字节(约585KB),直接从1.7MB砍到一半多。

进阶优化:针对0-25取值的位压缩

你提到每个byte的取值范围是0-25,这意味着每个值只需要5位二进制就能表示(2^5=32,完全覆盖0-25)。我们可以把多个5位值打包到更少的字节里,进一步压缩体积:

比如每8个0-25的原始值,原本占8字节,现在只需要5字节(8*5=40位,刚好是5个8位字节),压缩比能到62.5%。

写入示例代码(位压缩)

using System.IO;

public static void WritePatternsWithBitCompression(byte[][] patterns, string filePath)
{
    int patternLength = patterns[0].Length;
    int totalValues = patterns.Length * patternLength;
    
    using (var writer = new BinaryWriter(File.Open(filePath, FileMode.Create)))
    {
        writer.Write(patternLength);
        writer.Write(patterns.Length);
        
        int bitBuffer = 0;
        int bitsInBuffer = 0;
        
        foreach (var pattern in patterns)
        {
            foreach (byte b in pattern)
            {
                // 先校验值的范围,确保是0-25
                if (b > 25)
                    throw new ArgumentOutOfRangeException(nameof(b), "每个pattern元素必须在0-25之间");
                
                // 把当前值写入位缓冲区
                bitBuffer |= (b << bitsInBuffer);
                bitsInBuffer += 5;
                
                // 缓冲区满8位就写入一个字节
                while (bitsInBuffer >= 8)
                {
                    writer.Write((byte)(bitBuffer & 0xFF));
                    bitBuffer >>= 8;
                    bitsInBuffer -= 8;
                }
            }
        }
        
        // 写入缓冲区剩余的不足8位的内容
        if (bitsInBuffer > 0)
        {
            writer.Write((byte)bitBuffer);
        }
    }
}

读取示例代码(位压缩)

using System.IO;

public static byte[][] ReadPatternsWithBitCompression(string filePath)
{
    using (var reader = new BinaryReader(File.Open(filePath, FileMode.Open)))
    {
        int patternLength = reader.ReadInt32();
        int patternCount = reader.ReadInt32();
        int totalValues = patternCount * patternLength;
        
        var patterns = new byte[patternCount][];
        for (int i = 0; i < patternCount; i++)
        {
            patterns[i] = new byte[patternLength];
        }
        
        int bitBuffer = 0;
        int bitsInBuffer = 0;
        int valueIndex = 0;
        
        while (valueIndex < totalValues)
        {
            // 缓冲区不足5位时,读取新的字节补充
            if (bitsInBuffer < 5)
            {
                int nextByte = reader.ReadByte();
                bitBuffer |= (nextByte << bitsInBuffer);
                bitsInBuffer += 8;
            }
            
            // 提取5位的数值(0x1F是二进制5个1)
            byte value = (byte)(bitBuffer & 0x1F);
            patterns[valueIndex / patternLength][valueIndex % patternLength] = value;
            
            // 缓冲区右移5位,准备下一个值
            bitBuffer >>= 5;
            bitsInBuffer -= 5;
            valueIndex++;
        }
        
        return patterns;
    }
}

用这种位压缩方式,50000个长度为12的pattern(总共600,000个0-25的值),体积大概是375KB,比基础优化又小了近37%。

额外优化:重复pattern的去重

你提到集合里有重复的pattern(比如3个完全一样的),如果重复率高,还可以先对pattern去重,保存唯一的pattern列表,再用索引来标记每个位置的pattern,进一步缩小体积:

比如如果唯一pattern数量少于65536,用ushort(2字节)存索引,比存整个12字节的pattern省太多。

示例思路(去重+二进制存储)

// 需要自定义一个比较器来判断两个byte[]是否相等
public class ByteArrayComparer : IEqualityComparer<byte[]>
{
    public bool Equals(byte[] x, byte[] y)
    {
        return StructuralComparisons.StructuralEqualityComparer.Equals(x, y);
    }

    public int GetHashCode(byte[] obj)
    {
        int hash = 17;
        foreach (byte b in obj)
        {
            hash = hash * 31 + b;
        }
        return hash;
    }
}

// 写入方法
public static void WritePatternsWithDeduplication(byte[][] patterns, string filePath)
{
    var uniquePatterns = patterns.Distinct(new ByteArrayComparer()).ToList();
    // 把每个原始pattern映射到它在唯一列表中的索引
    var patternIndices = patterns.Select(p => (ushort)uniquePatterns.IndexOf(p)).ToArray();
    
    using (var writer = new BinaryWriter(File.Open(filePath, FileMode.Create)))
    {
        writer.Write(patterns[0].Length);
        writer.Write(uniquePatterns.Count);
        writer.Write(patterns.Length);
        
        // 写入所有唯一的pattern
        foreach (var pattern in uniquePatterns)
        {
            writer.Write(pattern);
        }
        
        // 写入索引数组
        foreach (var index in patternIndices)
        {
            writer.Write(index);
        }
    }
}

// 读取方法
public static byte[][] ReadPatternsWithDeduplication(string filePath)
{
    using (var reader = new BinaryReader(File.Open(filePath, FileMode.Open)))
    {
        int patternLength = reader.ReadInt32();
        int uniqueCount = reader.ReadInt32();
        int totalCount = reader.ReadInt32();
        
        // 读取所有唯一的pattern
        var uniquePatterns = new List<byte[]>();
        for (int i = 0; i < uniqueCount; i++)
        {
            uniquePatterns.Add(reader.ReadBytes(patternLength));
        }
        
        // 读取索引并还原原始集合
        var patterns = new byte[totalCount][];
        for (int i = 0; i < totalCount; i++)
        {
            ushort index = reader.ReadUInt16();
            patterns[i] = uniquePatterns[index];
        }
        
        return patterns;
    }
}

如果你的集合重复率很高(比如一半以上都是重复的),这种方式的体积会比之前的优化还要小很多。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 10:01:10