Сжатие / распаковка строки с C#
Я новичок в. net.я делаю сжатие и декомпрессию строки в C#. Существует XML, и я конвертирую в строку, и после этого я делаю сжатие и декомпрессию.В моем коде нет ошибки компиляции, за исключением случаев, когда я распаковываю свой код и возвращаю свою строку, возвращая только половину XML.
ниже мой код, пожалуйста, поправьте меня где я ошибаюсь.
код:
class Program
{
public static string Zip(string value)
{
//Transform string into byte[]
byte[] byteArray = new byte[value.Length];
int indexBA = 0;
foreach (char item in value.ToCharArray())
{
byteArray[indexBA++] = (byte)item;
}
//Prepare for compress
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.IO.Compression.GZipStream sw = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress);
//Compress
sw.Write(byteArray, 0, byteArray.Length);
//Close, DO NOT FLUSH cause bytes will go missing...
sw.Close();
//Transform byte[] zip data to string
byteArray = ms.ToArray();
System.Text.StringBuilder sB = new System.Text.StringBuilder(byteArray.Length);
foreach (byte item in byteArray)
{
sB.Append((char)item);
}
ms.Close();
sw.Dispose();
ms.Dispose();
return sB.ToString();
}
public static string UnZip(string value)
{
//Transform string into byte[]
byte[] byteArray = new byte[value.Length];
int indexBA = 0;
foreach (char item in value.ToCharArray())
{
byteArray[indexBA++] = (byte)item;
}
//Prepare for decompress
System.IO.MemoryStream ms = new System.IO.MemoryStream(byteArray);
System.IO.Compression.GZipStream sr = new System.IO.Compression.GZipStream(ms,
System.IO.Compression.CompressionMode.Decompress);
//Reset variable to collect uncompressed result
byteArray = new byte[byteArray.Length];
//Decompress
int rByte = sr.Read(byteArray, 0, byteArray.Length);
//Transform byte[] unzip data to string
System.Text.StringBuilder sB = new System.Text.StringBuilder(rByte);
//Read the number of bytes GZipStream red and do not a for each bytes in
//resultByteArray;
for (int i = 0; i < rByte; i++)
{
sB.Append((char)byteArray[i]);
}
sr.Close();
ms.Close();
sr.Dispose();
ms.Dispose();
return sB.ToString();
}
static void Main(string[] args)
{
XDocument doc = XDocument.Load(@"D:RSP.xml");
string val = doc.ToString(SaveOptions.DisableFormatting);
val = Zip(val);
val = UnZip(val);
}
}
мой размер XML составляет 63 КБ.
5 ответов:
код для сжатия и распаковки строки
public static void CopyTo(Stream src, Stream dest) { byte[] bytes = new byte[4096]; int cnt; while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0) { dest.Write(bytes, 0, cnt); } } public static byte[] Zip(string str) { var bytes = Encoding.UTF8.GetBytes(str); using (var msi = new MemoryStream(bytes)) using (var mso = new MemoryStream()) { using (var gs = new GZipStream(mso, CompressionMode.Compress)) { //msi.CopyTo(gs); CopyTo(msi, gs); } return mso.ToArray(); } } public static string Unzip(byte[] bytes) { using (var msi = new MemoryStream(bytes)) using (var mso = new MemoryStream()) { using (var gs = new GZipStream(msi, CompressionMode.Decompress)) { //gs.CopyTo(mso); CopyTo(gs, mso); } return Encoding.UTF8.GetString(mso.ToArray()); } } static void Main(string[] args) { byte[] r1 = Zip("StringStringStringStringStringStringStringStringStringStringStringStringStringString"); string r2 = Unzip(r1); }
помните, что
Zip
возвращает abyte[]
, в то время какUnzip
возвращает astring
. Если вы хотите строку сZip
вы можете base64 кодировать его (например, с помощьюConvert.ToBase64String(r1)
) (результатZip
очень двоичный! Это не то, что вы можете распечатать на экране или написать непосредственно в XML)предлагаемая версия предназначена для .NET 2.0, для .NET 4.0 используйте
MemoryStream.CopyTo
.важно: сжатое содержимое не может быть записано в выходной поток, пока
GZipStream
знает, что у него есть все входные данные (т. е. для эффективного сжатия ему нужны все данные). Вы должны убедиться, что выDispose()
наGZipStream
перед проверкой выходного потока (например,mso.ToArray()
). Это делается с помощьюusing() { }
блок выше. Обратите внимание, чтоGZipStream
является самым внутренним блоком, и содержимое доступно за его пределами. То же самое касается распаковка:Dispose()
наGZipStream
перед попыткой доступа к данным.
по данным этот фрагмент я использую этот код и он работает нормально:
using System; using System.IO; using System.IO.Compression; using System.Text; namespace CompressString { internal static class StringCompressor { /// <summary> /// Compresses the string. /// </summary> /// <param name="text">The text.</param> /// <returns></returns> public static string CompressString(string text) { byte[] buffer = Encoding.UTF8.GetBytes(text); var memoryStream = new MemoryStream(); using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true)) { gZipStream.Write(buffer, 0, buffer.Length); } memoryStream.Position = 0; var compressedData = new byte[memoryStream.Length]; memoryStream.Read(compressedData, 0, compressedData.Length); var gZipBuffer = new byte[compressedData.Length + 4]; Buffer.BlockCopy(compressedData, 0, gZipBuffer, 4, compressedData.Length); Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gZipBuffer, 0, 4); return Convert.ToBase64String(gZipBuffer); } /// <summary> /// Decompresses the string. /// </summary> /// <param name="compressedText">The compressed text.</param> /// <returns></returns> public static string DecompressString(string compressedText) { byte[] gZipBuffer = Convert.FromBase64String(compressedText); using (var memoryStream = new MemoryStream()) { int dataLength = BitConverter.ToInt32(gZipBuffer, 0); memoryStream.Write(gZipBuffer, 4, gZipBuffer.Length - 4); var buffer = new byte[dataLength]; memoryStream.Position = 0; using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) { gZipStream.Read(buffer, 0, buffer.Length); } return Encoding.UTF8.GetString(buffer); } } } }
С появлением .NET 4.0 (и выше) с потоком.Методы CopyTo (), я думал, что опубликую обновленный подход.
Я также думаю, что приведенная ниже версия полезна в качестве наглядного примера автономного класса для сжатия регулярных строк в кодированные строки Base64 и наоборот:
public static class StringCompression { /// <summary> /// Compresses a string and returns a deflate compressed, Base64 encoded string. /// </summary> /// <param name="uncompressedString">String to compress</param> public static string Compress(string uncompressedString) { byte[] compressedBytes; using (var uncompressedStream = new MemoryStream(Encoding.UTF8.GetBytes(uncompressedString))) { var compressedStream = new MemoryStream(); // setting the leaveOpen parameter to true to ensure that compressedStream will not be closed when compressorStream is disposed // this allows compressorStream to close and flush its buffers to compressedStream and guarantees that compressedStream.ToArray() can be called afterward // although MSDN documentation states that ToArray() can be called on a closed MemoryStream, this approach avoids relying on that very odd behavior should it ever change using (var compressorStream = new DeflateStream(compressedStream, CompressionLevel.Fastest, true)) { uncompressedStream.CopyTo(compressorStream); } // call compressedStream.ToArray() after the enclosing DeflateStream has closed and flushed its buffer to compressedStream compressedBytes = compressedStream.ToArray(); } return Convert.ToBase64String(compressedBytes); } /// <summary> /// Decompresses a deflate compressed, Base64 encoded string and returns an uncompressed string. /// </summary> /// <param name="compressedString">String to decompress.</param> public static string Decompress(string compressedString) { byte[] decompressedBytes; var compressedStream = new MemoryStream(Convert.FromBase64String(compressedString)); using (var decompressorStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) { using (var decompressedStream = new MemoryStream()) { decompressorStream.CopyTo(decompressedStream); decompressedBytes = decompressedStream.ToArray(); } } return Encoding.UTF8.GetString(decompressedBytes); }
вот еще один подход с использованием метода методов расширения для расширения класса String для добавления сжатия и декомпрессии строк. Вы можете опустить класс ниже в существующий проект, а затем использовать так:
var uncompressedString = "Hello World!"; var compressedString = uncompressedString.Compress();
и
var decompressedString = compressedString.Decompress();
а именно:
public static class Extensions { /// <summary> /// Compresses a string and returns a deflate compressed, Base64 encoded string. /// </summary> /// <param name="uncompressedString">String to compress</param> public static string Compress(this string uncompressedString) { byte[] compressedBytes; using (var uncompressedStream = new MemoryStream(Encoding.UTF8.GetBytes(uncompressedString))) { var compressedStream = new MemoryStream(); // setting the leaveOpen parameter to true to ensure that compressedStream will not be closed when compressorStream is disposed // this allows compressorStream to close and flush its buffers to compressedStream and guarantees that compressedStream.ToArray() can be called afterward // although MSDN documentation states that ToArray() can be called on a closed MemoryStream, this approach avoids relying on that very odd behavior should it ever change using (var compressorStream = new DeflateStream(compressedStream, CompressionLevel.Fastest, true)) { uncompressedStream.CopyTo(compressorStream); } // call compressedStream.ToArray() after the enclosing DeflateStream has closed and flushed its buffer to compressedStream compressedBytes = compressedStream.ToArray(); } return Convert.ToBase64String(compressedBytes); } /// <summary> /// Decompresses a deflate compressed, Base64 encoded string and returns an uncompressed string. /// </summary> /// <param name="compressedString">String to decompress.</param> public static string Decompress(this string compressedString) { byte[] decompressedBytes; var compressedStream = new MemoryStream(Convert.FromBase64String(compressedString)); using (var decompressorStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) { using (var decompressedStream = new MemoryStream()) { decompressorStream.CopyTo(decompressedStream); decompressedBytes = decompressedStream.ToArray(); } } return Encoding.UTF8.GetString(decompressedBytes); }
для тех, кто все еще получает магическое число в заголовке GZip неверно. Убедитесь, что вы проходите в потоке GZip. Ошибка и если ваша строка была застегнута с помощью php вам нужно сделать что-то вроде:
public static string decodeDecompress(string originalReceivedSrc) { byte[] bytes = Convert.FromBase64String(originalReceivedSrc); using (var mem = new MemoryStream()) { //the trick is here mem.Write(new byte[] { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0, 8); mem.Write(bytes, 0, bytes.Length); mem.Position = 0; using (var gzip = new GZipStream(mem, CompressionMode.Decompress)) using (var reader = new StreamReader(gzip)) { return reader.ReadToEnd(); } } }
Это обновленная версия для .NET 4.5 и новее с использованием async / await и IEnumerables:
public static class CompressionExtensions { public static async Task<IEnumerable<byte>> Zip(this object obj) { byte[] bytes = obj.Serialize(); using (MemoryStream msi = new MemoryStream(bytes)) using (MemoryStream mso = new MemoryStream()) { using (var gs = new GZipStream(mso, CompressionMode.Compress)) await msi.CopyToAsync(gs); return mso.ToArray().AsEnumerable(); } } public static async Task<object> Unzip(this byte[] bytes) { using (MemoryStream msi = new MemoryStream(bytes)) using (MemoryStream mso = new MemoryStream()) { using (var gs = new GZipStream(msi, CompressionMode.Decompress)) { //gs.CopyTo(mso); await gs.CopyToAsync(mso); } return mso.ToArray().Deserialize(); } } } public static class SerializerExtensions { /// <summary> /// Writes the given object instance to a binary file. /// <para>Object type (and all child types) must be decorated with the [Serializable] attribute.</para> /// <para>To prevent a variable from being serialized, decorate it with the [NonSerialized] attribute; cannot be applied to properties.</para> /// </summary> /// <typeparam name="T">The type of object being written to the XML file.</typeparam> /// <param name="filePath">The file path to write the object instance to.</param> /// <param name="objectToWrite">The object instance to write to the XML file.</param> /// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param> public static byte[] Serialize<T>(this T objectToWrite) { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(stream, objectToWrite); return stream.GetBuffer(); } } /// <summary> /// Reads an object instance from a binary file. /// </summary> /// <typeparam name="T">The type of object to read from the XML.</typeparam> /// <param name="filePath">The file path to read the object instance from.</param> /// <returns>Returns a new instance of the object read from the binary file.</returns> public static async Task<T> _Deserialize<T>(this byte[] arr) { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter binaryFormatter = new BinaryFormatter(); await stream.WriteAsync(arr, 0, arr.Length); stream.Position = 0; return (T)binaryFormatter.Deserialize(stream); } } public static async Task<object> Deserialize(this byte[] arr) { object obj = await arr._Deserialize<object>(); return obj; } }
С помощью этого вы можете сериализовать все BinaryFormatter поддерживает, а не только строки.