C#-如何以尽可能小的大小将字节值保存到文件中?
我需要以尽可能小的文件大小序列化以下数据 我有一组模式,每个模式都是一个固定长度的字节数组(C#-如何以尽可能小的大小将字节值保存到文件中?,c#,arrays,serialization,C#,Arrays,Serialization,我需要以尽可能小的文件大小序列化以下数据 我有一组模式,每个模式都是一个固定长度的字节数组(byte[]) 在本例中,让我们使用5的模式长度,因此字节数组将为: var pattern = new byte[] {1, 2, 3, 4, 5}; 假设我们在一个集合中有3个相同的模式: var collection = new byte[][] { pattern, pattern, pattern }; 目前,我正在将集合保存在ASCII编码的文件中。使用上述集合,保存的文件如下所示: 01
byte[]
)
在本例中,让我们使用5的模式长度,因此字节数组将为:
var pattern = new byte[] {1, 2, 3, 4, 5};
假设我们在一个集合中有3个相同的模式:
var collection = new byte[][] { pattern, pattern, pattern };
目前,我正在将集合保存在ASCII编码的文件中。使用上述集合,保存的文件如下所示:
010203040501020304050102030405
数组中的每个字节由2位数字(00)表示,因此我可以处理从0到25的字节值,它可以如下所示:
010203040501020304050102030405
[01 | 02 | 03 | 04 | 05][01 | 02 | 03 | 04 | 05][01 | 02 | 03 | 04 | 05]
当我反序列化文件时,我将每个2个字符的块解析为一个字节,并将每5个字节放入一个字节数组
据我所知,ASCII编码文件中的每个字符都是一个字节——可能提供256个不同的值,但我所需要的是,每个2个字符的块都可能是0到25之间的十进制值
当我保存一个有50000个模式的文件时,每个模式的长度为12,我最终得到一个1.7MB的文件,这个文件太大了
我可以在C#中使用什么编码使文件大小更小
请提供如何在文件中写入和读取此数据的示例代码。我在将二进制数据编码为条形码时做了类似的操作(请参阅)。考虑下面的代码,这些代码会将样本序列化到文件中并立即反序列化:
static void Main(string[] args)
{
var data = new List<byte[]>() {
new byte[] { 01, 05, 15, 04, 11, 00, 01, 01, 05, 15, 04, 11, 00, 01 },
new byte[] { 09, 04, 02, 00, 08, 12, 01, 07, 04, 02, 00, 08, 12, 01 },
new byte[] { 01, 05, 06, 04, 02, 00, 01, 01, 05, 06, 04, 02, 00, 01 }
};
// has to be known when loading the file
var reasonableBase = data.SelectMany(i => i).Max() + 1;
using (var target = File.OpenWrite("data.bin"))
{
using (var writer = new BinaryWriter(target))
{
// write the number of lines (16 bit, lines limited to 65536)
writer.Write((ushort)data.Count);
// write the base (8 bit, base limited to 255)
writer.Write((byte)reasonableBase);
foreach (var sample in data)
{
// converts the byte array into a large number of the known base (bypasses all the bit-mess)
var serializedData = ByteArrayToNumberBased(sample, reasonableBase).ToByteArray();
// write the length of the sample (8 bit, limited to 255)
writer.Write((byte)serializedData.Length);
writer.Write(serializedData);
}
}
}
var deserializedData = new List<byte[]>();
using (var source = File.OpenRead("data.bin"))
{
using (var reader = new BinaryReader(source))
{
var lines = reader.ReadUInt16();
var sourceBase = reader.ReadByte();
for (int i = 0; i < lines; i++)
{
var length = reader.ReadByte();
var value = new BigInteger(reader.ReadBytes(length));
// chunk the bytes back of the big number we loaded
// works because we know the base
deserializedData.Add(NumberToByteArrayBased(value, sourceBase));
}
}
}
}
private static BigInteger ByteArrayToNumberBased(byte[] data, int numBase)
{
var result = BigInteger.Zero;
for (int i = 0; i < data.Length; i++)
{
result += data[i] * BigInteger.Pow(numBase, i);
}
return result;
}
private static byte[] NumberToByteArrayBased(BigInteger data, int numBase)
{
var list = new List<Byte>();
do
{
list.Add((byte)(data % numBase));
}
while ((data = (data / numBase)) > 0);
return list.ToArray();
}
static void Main(字符串[]args)
{
变量数据=新列表(){
新字节[]{01,05,15,04,11,00,01,01,05,15,04,11,00,01},
新字节[]{09,04,02,00,08,12,01,07,04,02,00,08,12,01},
新字节[]{01,05,06,04,02,00,01,01,05,06,04,02,00,01}
};
//加载文件时必须知道
var reasonalbase=data.SelectMany(i=>i).Max()+1;
使用(var target=File.OpenWrite(“data.bin”))
{
使用(var writer=newbinarywriter(目标))
{
//写入行数(16位,行数限制为65536)
writer.Write((ushort)data.Count);
//写入基数(8位,基数限制为255)
writer.Write((字节)合理基数);
foreach(数据中的var样本)
{
//将字节数组转换为大量已知基(绕过所有位混乱)
var serializedData=ByteArrayToNumberBased(示例,ReasonicalBase).ToByteArray();
//写入样本的长度(8位,限制为255)
writer.Write((字节)serializedData.Length);
writer.Write(序列化数据);
}
}
}
var deserializedData=新列表();
使用(var source=File.OpenRead(“data.bin”))
{
使用(变量读取器=新二进制读取器(源))
{
var lines=reader.ReadUInt16();
var sourceBase=reader.ReadByte();
对于(int i=0;i0);
return list.ToArray();
}
与您的格式相比,示例数据将序列化为27字节,而不是90字节。使用@xanatos的每个符号4.7位,完美的结果将是
14*3*4.7/8=24675字节
,所以这并不坏(公平地说:示例序列化为30字节,基数设置为26).下面是一个示例,说明如何使用GZipStream
和BinaryFormatter
从压缩文件中读取和写入数据
它对于小型阵列不是非常有效,但对于大型阵列则更加有效。但是,请注意,这依赖于数据是可压缩的-如果不是,那么这将没有任何用处
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
namespace Demo
{
static class Program
{
static void Main()
{
var pattern = new byte[] { 1, 2, 3, 4, 5 };
var collection = new [] { pattern, pattern, pattern };
string filename = @"e:\tmp\test.bin";
zipToFile(filename, collection);
var deserialised = unzipFromFile(filename);
Console.WriteLine(string.Join("\n", deserialised.Select(row => string.Join(", ", row))));
}
static void zipToFile(string file, byte[][] data)
{
using (var output = new FileStream(file, FileMode.Create))
using (var gzip = new GZipStream(output, CompressionLevel.Optimal))
{
new BinaryFormatter().Serialize(gzip, data);
}
}
static byte[][] unzipFromFile(string file)
{
using (var input = new FileStream(file, FileMode.Open))
using (var gzip = new GZipStream(input, CompressionMode.Decompress))
{
return (byte[][]) new BinaryFormatter().Deserialize(gzip);
}
}
}
}
有时简单是最好的折衷办法 矩形阵列可以看作是一系列线性阵列 字节文件是字节的线性数组 下面是一段非常简单的代码,用于转换字节的矩形数组并将字节写入文件:
// All patterns must be the same length so they can be split when reading
File.WriteAllBytes(Path.GetTempFileName(), collection.SelectMany(p => p).ToArray());
System.Linq.Enumerable.SelectMany(pattern=>pattern)
获取序列并将其展平为序列。(它和ToArray()并不是最有效的,但对于50000*4个元素,它可能很好。)
考虑到这一点,如果需要压缩,Zip将是一种可行的方法 如果您的文件不必是ASCII,您可以使用和来压缩数据。文件必须是文本编辑器可读的吗?只需保存字节而不是数字,就可以保存一半的数据?我也无意冒犯,但这听起来很像家庭作业。0-25->A。。Z.或使用每5位表示一个值的位流。使用进行压缩/解压缩的流可能更容易。通过二进制写入,最多可以压缩5位/数字(log2(26)=4.7)。。。但是写基于位的信息很痛苦谢谢你的回复,我得花点时间看一下。谢谢,我会试试看的