190

我是.net的新手。我正在用C#压缩和解压缩字符串。有一个XML,我在字符串中转换,然后进行压缩和解压缩。我的代码中没有编译错误,除非我解压缩代码并返回字符串,它只返回一半的XML。

下面是我的代码,请纠正我的错误。

代码:

课程计划{公共静态字符串Zip(字符串值){//将字符串转换为字节[]byte[]字节数组=新字节[value.Length];int索引BA=0;foreach(value.ToCharArray()中的字符项){byteArray[indexBA++]=(字节)项;}//准备压缩系统。IO.MemoryStream ms=新系统。IO.MemoryStream();系统。IO.压缩。GZipStream sw=新系统。IO.压缩。GZipStream(毫秒,System.IO.Compression。CompressionMode.Compression);//压缩西南部。写入(byteArray,0,byteArray.Length);//关闭,请勿刷新,因为字节将丢失。。。西南部。关闭();//将字节[]zip数据转换为字符串byteArray=毫秒ToArray();系统。文本。StringBuilder sB=新系统。文本。StringBuilder(byteArray.Length);foreach(字节数组中的字节项){sB.Append((char)item);}毫秒关闭();西南部。处置();ms.Dispose();return sB.ToString();}公共静态字符串UnZip(字符串值){//将字符串转换为字节[]byte[]字节数组=新字节[value.Length];int索引BA=0;foreach(value.ToCharArray()中的字符项){byteArray[indexBA++]=(字节)项;}//准备减压系统。IO.MemoryStream ms=新系统。IO.MemoryStream(字节数组);系统。IO.压缩。GZipStream sr=新系统。IO.压缩。GZipStream(毫秒,系统。IO压缩。压缩模式。减压);//重置变量以收集未压缩的结果byteArray=新字节[byteArray.Length];//解压缩int rByte=sr.Read(byteArray,0,byteArray.Length);//转换byte[]将数据解压缩为字符串系统。文本。StringBuilder sB=新系统。文本。字符串生成器(rByte);//读取GZipStream red的字节数,而不是中的每个字节//resultByteArray;for(int i=0;i<rByte;i++){sB.Append((char)byteArray[i]);}sr.关闭();毫秒关闭();sr.处置();ms.Dispose();return sB.ToString();}static void Main(字符串[]参数){XDocument文档=XDocument。加载(@“D:\RSP.xml”);字符串val=文档。ToString(SaveOptions.DisableFormatting);val=邮政编码(val);val=解压缩(val);}}

我的XML大小是63KB。

4
  • 2
    我怀疑如果使用UTF8编码(或UTF16或诸如此类)和GetBytes/GetString。这也将大大简化代码。还建议使用使用.
    – 用户166390
    评论 2011年9月8日5:36
  • 1
    你不能像你那样把字符转换成字节,反之亦然(使用简单的转换)。您需要使用编码,并使用相同的编码进行压缩/解压缩。请参阅下面的xanatos答案。 评论 2011年9月8日6:10
  • @pst不,不会的;你会用编码走错了路。根据克萨纳托斯的回答,这里你需要64垒 评论 2011年9月8日6:18
  • @Marc Gravell True错过了签名/意图的那部分。绝对不是我首选的签名。
    – 用户166390
    评论 2011年9月8日7:06

8个答案8

重置为默认值
327

压缩/解压缩字符串的代码

public static void CopyTo(流src,流dest){byte[]字节=新字节[4096];国际碳纳米管;while((cnt=src.Read(字节,0,字节长度))!=0) {目的地。写入(字节,0,cnt);}}公共静态字节[]Zip(string str){var bytes=编码。UTF8.获取字节(str);使用(var msi=新内存流(字节))using(var mso=new MemoryStream()){using(var gs=新GZipStream(mso,CompressionMode.Compress)){//msi。抄送(gs);CopyTo(msi,gs);}返回mso。ToArray();}}公共静态字符串解压缩(字节[]字节){using(var msi=新内存流(字节))using(var mso=new MemoryStream()){using(var gs=新GZipStream(msi,CompressionMode.Decompress)){//gs.复制到(mso);CopyTo(gs,mso);}return编码。UTF8.GetString(mso.ToArray());}}static void Main(字符串[]参数){byte[]r1=Zip(“StringStringString字符串StringStringeStringStriNGStringStriingStringStrinkStringStriNG”);字符串r2=解压缩(r1);}

记住拉链返回一个字节[],同时解压缩返回一个一串。如果要从中获取字符串拉链您可以对其进行Base64编码(例如,使用转换。ToBase64字符串(r1))(结果拉链是非常二进制的!它不是可以打印到屏幕上或直接用XML编写的东西)

建议的版本适用于。NET 2.0,用于。NET 4.0使用内存流。方法.

重要事项:压缩内容无法写入输出流,直到GZipStream公司知道它有所有的输入(也就是说,为了有效地压缩它需要所有的数据)。你需要确保处置()GZipStream公司在检查输出流(例如。,mso公司。ToArray()). 这是用使用(){}块。请注意GZipStream公司是最里面的块,内容在它外面访问。解压缩也是这样:处置()GZipStream公司在尝试访问数据之前。

20
  • 谢谢你的回复。当我使用你的代码时,它给了我编译错误。“CopyTo()没有命名空间或程序集引用。”。之后,我在谷歌上搜索并找到了它的CopyTo()部分。NET 4框架。但我正在使用.NET 2.0和3.5框架。请建议我。:) 评论 2011年9月8日7:19
  • 1
    这是在.net4.5上进行压缩的最有效方法吗? 评论 2014年9月14日14:06
  • 1
    请注意,如果字符串包含代理项对,这将失败(unzipped-string!=original),例如。string s=“X\uD800Y”。我注意到,如果我们将“编码”更改为UTF7,它会起作用……但使用UTF7时,我们确定可以表示所有字符吗? 评论 2015年1月15日11:17
  • 1
    @我检查过的Pan.student,它似乎与我生成的gz文件一起工作。这个文件可能不是真正的gz文件。请注意,gz文件不是rar文件,不是zip文件,也不是bz2文件。它们都是不兼容的格式。如果你能从Windows打开它,那么我建议你在SO发布你正在使用的代码时发布一个问题。 评论 2015年4月13日7:43
  • 1
    正如@rluks所说,答案中的代码将导致异常“GZip头中的幻数不正确。请确保传入GZip流。”。 评论 2017年8月28日8:00
139

根据这个片段我使用这个代码,它工作正常:

使用系统;使用系统。信息作战;使用系统。IO.压缩;使用系统。文本;命名空间CompressString{内部静态类StringCompressor{///<摘要>///压缩字符串。///</summary>///<param name=“text”>文本</参数>///<返回>公共静态字符串CompressString(字符串文本){byte[]buffer=编码。UTF8.获取字节(文本);var memoryStream=新的memoryStream();using(var gZipStream=新的GZipStreat(memoryStream,CompressionMode.Compress,true)){gZipStream。写入(buffer,0,buffer.Length);}内存流。位置=0;var compressedData=新字节[memoryStream.Length];内存流。读取(compressedData,0,compressedData.Length);var gZipBuffer=新字节[compressedData.Length+4];缓冲区。块复制(compressedData,0,gZipBuffer,4,compressedData.Length);缓冲区。块复制(BitConverter.GetBytes(buffer.Length),0,gZipBuffer,0,4);return转换。ToBase64String(gZipBuffer);}///<摘要>///解压缩字符串。///</summary>///<param name=“compressedText”>压缩文本</参数>///<返回>公共静态字符串DecompressString(string compressedText){byte[]gZipBuffer=转换。来自Base64String(压缩文本);使用(var memoryStream=新的memoryStream()){int dataLength=位转换器。ToInt32(gZipBuffer,0);内存流。写入(gZipBuffer,4,gZipBuffer.Length-4);var buffer=新字节[dataLength];内存流。位置=0;使用(var gZipStream=新gZipStream(memoryStream,CompressionMode.Descress)){gZipStream。读取(buffer,0,buffer.Length);}return编码。UTF8.GetString(缓冲区);}}}}
11
  • 我只是想感谢你发布这个代码。我把它放进了我的项目中,它开箱即用,没有任何问题。 评论 2015年7月27日17:47
  • 4
    是的,跳出盒子!我还喜欢将长度添加为前四个字节的想法 评论 2016年3月21日13:51
  • 2
    这是最好的答案。这个应该标记为答案! 评论 2017年8月28日7:59
  • 1
    @Matt这就像压缩.zip文件-.png已经是压缩内容了
    – 富波
    评论 2017年12月5日7:15
  • 4
    标记为答案的答案不稳定。这是最好的答案。 评论 2018年5月5日5:43
67

随着的到来。NET 4.0(及更高版本)。CopyTo()方法,我想我会发布一个更新的方法。

我还认为以下版本非常有用,它是一个自包含类的清晰示例,用于将常规字符串压缩为Base64编码的字符串,反之亦然:

公共静态类StringCompression{///<摘要>///压缩字符串并返回deflate压缩的Base64编码字符串。///</summary>///要压缩的字符串公共静态字符串Compress(string uncompressedString){byte[]压缩字节;using(var uncompessedStream=new MemoryStream(编码.UTF8.GetBytes(uncompressedString)){using(var compressedStream=new MemoryStream()){ //将leaveOpen参数设置为true,以确保在释放compressorStream时compressedStream不会关闭//这允许compressorStream关闭其缓冲区并将其刷新到compressedStream,并确保compressedStream。之后可以调用ToArray()//尽管MSDN文档声明ToArray()可以在关闭的MemoryStream上调用,但我不想依赖这种非常奇怪的行为using(var compressorStream=新的DeflateStream(compressedStream,CompressionLevel.Fastest,true)){uncompressedStream。CopyTo(压缩流);}//调用compressedStream。封闭DeflateStream关闭并将其缓冲区刷新为compressedStream后的ToArray()compressedBytes=压缩流。ToArray();}}return转换。ToBase64String(压缩字节);}///<摘要>///解压缩deflate压缩的Base64编码字符串,并返回未压缩的字符串。///</summary>///<param name=“compressedString”>要解压缩的字符串</参数>公共静态字符串解压缩(string compressedString){byte[]解压缩字节;var compressedStream=新的MemoryStream(Convert.FromBase64String(compressedString));using(var decompressorStream=新的DeflateStream(compressedStream,CompressionMode.Decompress)){using(var decompressedStream=new MemoryStream()){解压缩或流。CopyTo(解压缩流);decompressedBytes=解压缩流。ToArray();}}return编码。UTF8.GetString(解压缩字节);}}

下面是使用扩展方法技术扩展String类以添加字符串压缩和解压缩的另一种方法。您可以将以下类拖放到现有项目中,然后使用:

var uncompressedString=“Hello World!”;var compressedString=解压缩String.Compress();

var decompressedString=压缩字符串.Decompress();

即:

公共静态类扩展{///<摘要>///压缩字符串并返回deflate压缩的Base64编码字符串。///</summary>///要压缩的字符串公共静态字符串Compress(此字符串为uncompressedString){byte[]压缩字节;using(var uncompessedStream=new MemoryStream(编码.UTF8.GetBytes(uncompressedString)){using(var compressedStream=new MemoryStream()){ //将leaveOpen参数设置为true,以确保在释放compressorStream时compressedStream不会关闭//这允许compressorStream关闭其缓冲区并将其刷新到compressedStream,并确保compressedStream。之后可以调用ToArray()//尽管MSDN文档声明ToArray()可以在关闭的MemoryStream上调用,但我不想依赖这种非常奇怪的行为using(var compressorStream=新的DeflateStream(compressedStream,CompressionLevel.Fastest,true)){uncompressedStream。CopyTo(压缩流);}//调用compressedStream。封闭的DeflateStream关闭并将其缓冲区刷新为compressedStream之后的ToArray()compressedBytes=压缩流。ToArray();}}return转换。ToBase64String(压缩字节);}///<摘要>///解压缩deflate压缩的Base64编码字符串,并返回未压缩的字符串。///</summary>///<param name=“compressedString”>要解压缩的字符串</参数>公共静态字符串解压缩(此字符串compressedString){byte[]解压缩字节;var compressedStream=新内存流(Convert.FromBase64String(compressedString));using(var decompressorStream=新的DeflateStream(compressedStream,CompressionMode.Decompress)){using(var decompressedStream=new MemoryStream()){解压缩或流。CopyTo(解压缩流);解压缩字节=解压缩流。ToArray();}}return编码。UTF8.GetString(解压缩字节);}}
11
  • 2
    杰斯:我想你失踪了使用MemoryStream实例的语句。对于F#开发人员:避免使用关键字使用对于compressorStream/decompressorStream实例,因为它们需要在ToArray()被呼叫
    – 纽克特
    评论 2018年8月15日17:13
  • 1
    使用GZipStream会不会更好,因为它添加了一些额外的验证?GZipStream还是DeflateStream类? 评论 2018年10月1日7:29
  • 2
    @迈克尔·弗雷德吉姆(Michael Freidgeim):我认为压缩和解压缩内存流是不可能的。对于文件或不可靠的传输,这是有意义的。我要说的是,在我的特定用途中,高速是非常理想的,因此我可以避免任何开销。
    – 杰斯
    评论 2018年11月22日3:58
  • 1
    工作很好,但您应该在使用后处理内存流,或者按照@knocte的建议使用每个流 评论 2019年4月24日11:06
  • 1
    @塞巴斯蒂安阅读了文档:learn.microsoft.com/en-us/dotnet/api/…默认情况下,DeflateStream拥有基础流,因此关闭流也会关闭基础流
    – 杰斯
    评论 2019年4月25日16:14
25

我最喜欢@fubo的答案,但我认为这要优雅得多。

此方法更兼容,因为它不预先手动存储长度。

此外,我还公开了支持字符串到字符串、字节[]到字节[]和流到流压缩的扩展。

公共静态类ZipExtensions{公共静态字符串CompressToBase64(此字符串数据){return转换。ToBase64String(编码.UTF8.GetBytes(数据))。压缩());}公共静态字符串DecompressFromBase64(此字符串数据){return编码。UTF8.GetString(转换.FromBase64String(数据)。解压缩();}公共静态字节[]压缩(此字节[]数据){using(var sourceStream=新内存流(数据))using(var destinationStream=new MemoryStream()){源流。压缩到(destinationStream);return destinationStream。ToArray();}}公共静态字节[]解压缩(此字节[]数据){using(var sourceStream=新内存流(数据))using(var destinationStream=new MemoryStream()){源流。解压缩到(destinationStream);return destinationStream。ToArray();}}public static void CompressTo(此Stream流,Stream outputStream){using(var gZipStream=新的GZipStreat(outputStream,CompressionMode.Compress)){流。CopyTo(gZipStream);gZipStream。冲洗();}}public static void DecompressTo(此Stream流,Stream outputStream){using(var gZipStream=新的GZipStreat(stream,CompressionMode.Decompress)){gZipStream。CopyTo(outputStream);}}}
1
  • 我使用52 MB的文件测试了这个解决方案与Jace的解决方案。Jace的解决方案将其压缩到2.9 MB,压缩和解压缩需要361毫秒。此解决方案将其压缩到1.66 MB,但压缩和解压缩需要788毫秒。两者使用的内存量大致相同。 评论 2022年12月2日20:06
13

这是的更新版本。NET 4.5及更高版本,使用异步/等待和IEnumerables:

公共静态类CompressionExtensions{公共静态异步任务<IEnumerable<byte>>Zip(此对象obj){byte[]字节=obj.Serialize();using(MemoryStream msi=新的MemoryStrem(字节))using(MemoryStream mso=new MemoryStream()){using(var gs=新GZipStream(mso,CompressionMode.Compress))等待msi。复制到异步(gs);返回mso。ToArray()。AsEnumerable();}}公共静态异步任务<object>Unzip(this byte[]bytes){using(MemoryStream msi=新的MemoryStrem(字节))using(MemoryStream mso=new MemoryStream()){using(var gs=新GZipStream(msi,CompressionMode.Decompress)){//同步示例://gs.复制到(mso);//异步方式(注意在方法定义中使用Async关键字)等待gs.CopyToAsync(mso);}返回mso。ToArray()。反序列化();}}}公共静态类SerializerExtensions{公共静态字节[]序列化<T>(此T对象ToWrite){using(MemoryStream流=new MemoryStream()){BinaryFormatter BinaryFormatter=新的BinaryFormatter();二进制格式设置工具。序列化(流,objectToWrite);回流。GetBuffer();}}公共静态异步任务<T>_串行化<T>(此字节[]arr){using(MemoryStream流=new MemoryStream()){BinaryFormatter BinaryFormatter=新的BinaryFormatter();等待流。WriteAsync(arr,0,arr.Length);流。位置=0;return(T)二进制格式设置工具。反序列化(流);}}public static async Task<object>反序列化(this byte〔〕arr){object obj=等待arr._Deserialize<对象>();返回对象;}}

有了它,您可以序列化所有内容二进制格式支持,而不是字符串。

编辑:

万一你需要照顾编码,你可以使用转换。ToBase64String(字节[])...

如果你需要一个例子,看看这个答案!

  • 在对样本进行反序列化和编辑之前,必须重置流位置。此外,您的XML注释也是无关的。
    – 马格森
    评论 2018年4月18日14:37
  • 值得注意的是,这种方法有效,但仅适用于基于UTF8的东西。例如,如果您在序列化/反序列化的字符串值中添加类似于aäö的瑞典字符,它将无法通过往返测试:/
    – bc3技术
    评论 2019年8月21日12:32
  • 在这种情况下,您可以使用转换。ToBase64String(字节[])请看这个答案(stackoverflow.com/a/23908465/3286975). 希望它能有所帮助! 评论 2019年8月22日2:31
7

对于那些仍然得到GZip头中的幻数不正确。确保你是通过GZip流。错误如果你的绳子是用拉链拉的php(电话)您需要执行以下操作:

公共静态字符串decodeDecompress(string originalReceivedSrc){byte[]字节=转换。来自Base64String(原始接收Src);using(var mem=new MemoryStream()){//诀窍就在这里成员。写入(新字节[]{0x1f、0x8b、0x08、0x00、0x00,0x00、x00}、0、8);成员。写入(字节,0,字节。长度);成员。位置=0;using(var gzip=新的GZipStream(mem,CompressionMode.Decompress))using(var reader=新StreamReader(gzip)){返回读取器。ReadToEnd();}}}
2
  • 我得到这个异常:抛出异常:“System。System.dll中的IO.InvalidDataException“其他信息:GZip页脚中的CRC与从解压缩数据计算的CRC不匹配。 评论 2016年9月2日9:02
  • 我相信有人会面临同样的问题。要在压缩字符串中使用这个神奇的头,需要使用适当的php函数:“gzencode”而不是“gzcompress”。PHP中还有另一种压缩算法:“gziflate”,但我个人从未使用过。附言:您的代码有一个问题:您编写了一个标头,然后通过将偏移量0设置为第二个Write()方法,用实际字节覆盖它,因此流中的字节相同。
    – 开发商
    评论 2021年9月10日5:28
5

对于.net6跨平台压缩/解压缩字符串,使用C#,使用SharpZipLib库。测试ubuntu(18.0.x)和windows。

#区域辅助对象私有字节[]Zip(字符串文本){if(文本==空)返回null;字节[]ret;using(var outputMemory=new MemoryStream()){using(var gz=新GZipStream(outputMemory,CompressionLevel.Optimal)){using(var sw=新StreamWriter(gz,Encoding.UTF8)){西南部。写(文本);}}ret=输出内存。ToArray();}返回ret;}私有字符串解压缩(字节[]字节){字符串ret=空;using(var inputMemory=新内存流(字节)){using(var gz=新GZipStream(inputMemory,CompressionMode.Decompress)){using(var sr=新StreamReader(gz,Encoding.UTF8)){ret=sr.ReadToEnd();}}}返回ret;}#端部区域
4

我们可以通过使用StreamReader和StreamWriter来降低代码复杂性,而不是手动将字符串转换为字节数组。您只需要三条流:

公共静态字节[]Zip(字符串未压缩){字节[]ret;using(var outputMemory=new MemoryStream()){using(var gz=新GZipStream(outputMemory,CompressionLevel.Optimal)){using(var sw=新StreamWriter(gz,Encoding.UTF8)){软件。写入(未压缩);}}ret=输出内存。ToArray();}返回ret;}公共静态字符串解压缩(字节[]已压缩){字符串ret=空;using(var inputMemory=new MemoryStream(压缩)){using(var gz=新GZipStream(inputMemory,CompressionMode.Decompress)){using(var sr=新StreamReader(gz,Encoding.UTF8)){ret=sr.ReadToEnd();}}}返回ret;}
1
  • 我试过了,但在某些情况下,这导致了问题。我甚至尝试过转换。UTF8,但在某些情况下也有问题。唯一100%可行的解决方案是简单地进行for循环,手动构建字符串,以及将字符串转换为字节。
    – 开发商
    评论 2021年9月10日5:04

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.