.Net과 호환되는 GZIPOutputStream을 사용하여 문자열을 압축 및 압축 해제하려면 어떻게해야합니까?
Android에서 GZip을 사용하여 문자열을 압축하는 예제가 필요합니다. 메서드에 "hello"와 같은 문자열을 보내고 다음 압축 문자열을 얻고 싶습니다.
BQAAAB + LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee ++ 997o7nU4n99 / ALmeVlnw / wAAPyqyB1xmWAAFs9s5K2smeWa
그런 다음 압축을 풀어야합니다. 아무도 나에게 예제를 제공하고 다음 방법을 완료 할 수 있습니까?
private String compressString(String input) {
//...
}
private String decompressString(String input) {
//...
}
감사,
최신 정보
선배의 대답 에 따르면 이제 다음과 같은 4 가지 방법이 있습니다. Android 및 .net 압축 및 압축 해제 방법. 이러한 방법은 한 가지 경우를 제외하고 서로 호환됩니다. 첫 번째 3 개 상태에서는 호환되지만 4 번째 상태에서는 호환되지 않습니다.
- 상태 1) Android.compress <-> Android.decompress : ( 확인 )
- 상태 2) Net.compress <-> Net.decompress : ( 확인 )
- 상태 3) Net.compress-> Android.decompress : ( 확인 )
- 상태 4) Android.compress-> .Net.decompress : ( NOT OK )
아무도 그것을 해결할 수 있습니까?
Android 방법 :
public static String compress(String str) throws IOException {
byte[] blockcopy = ByteBuffer
.allocate(4)
.order(java.nio.ByteOrder.LITTLE_ENDIAN)
.putInt(str.length())
.array();
ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(str.getBytes());
gos.close();
os.close();
byte[] compressed = new byte[4 + os.toByteArray().length];
System.arraycopy(blockcopy, 0, compressed, 0, 4);
System.arraycopy(os.toByteArray(), 0, compressed, 4,
os.toByteArray().length);
return Base64.encode(compressed);
}
public static String decompress(String zipText) throws IOException {
byte[] compressed = Base64.decode(zipText);
if (compressed.length > 4)
{
GZIPInputStream gzipInputStream = new GZIPInputStream(
new ByteArrayInputStream(compressed, 4,
compressed.length - 4));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int value = 0; value != -1;) {
value = gzipInputStream.read();
if (value != -1) {
baos.write(value);
}
}
gzipInputStream.close();
baos.close();
String sReturn = new String(baos.toByteArray(), "UTF-8");
return sReturn;
}
else
{
return "";
}
}
.Net 메서드 :
public static string compress(string text)
{
byte[] buffer = Encoding.UTF8.GetBytes(text);
MemoryStream ms = new MemoryStream();
using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
{
zip.Write(buffer, 0, buffer.Length);
}
ms.Position = 0;
MemoryStream outStream = new MemoryStream();
byte[] compressed = new byte[ms.Length];
ms.Read(compressed, 0, compressed.Length);
byte[] gzBuffer = new byte[compressed.Length + 4];
System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
return Convert.ToBase64String(gzBuffer);
}
public static string decompress(string compressedText)
{
byte[] gzBuffer = Convert.FromBase64String(compressedText);
using (MemoryStream ms = new MemoryStream())
{
int msgLength = BitConverter.ToInt32(gzBuffer, 0);
ms.Write(gzBuffer, 4, gzBuffer.Length - 4);
byte[] buffer = new byte[msgLength];
ms.Position = 0;
using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
{
zip.Read(buffer, 0, buffer.Length);
}
return Encoding.UTF8.GetString(buffer);
}
}
GZIP 방법 :
public static byte[] compress(String string) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(string.getBytes());
gos.close();
byte[] compressed = os.toByteArray();
os.close();
return compressed;
}
public static String decompress(byte[] compressed) throws IOException {
final int BUFFER_SIZE = 32;
ByteArrayInputStream is = new ByteArrayInputStream(compressed);
GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
StringBuilder string = new StringBuilder();
byte[] data = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = gis.read(data)) != -1) {
string.append(new String(data, 0, bytesRead));
}
gis.close();
is.close();
return string.toString();
}
그리고 테스트 :
final String text = "hello";
try {
byte[] compressed = compress(text);
for (byte character : compressed) {
Log.d("test", String.valueOf(character));
}
String decompressed = decompress(compressed);
Log.d("test", decompressed);
} catch (IOException e) {
e.printStackTrace();
}
=== 업데이트 ===
.Net 호환성이 필요한 경우 코드를 약간 변경해야합니다.
public static byte[] compress(String string) throws IOException {
byte[] blockcopy = ByteBuffer
.allocate(4)
.order(java.nio.ByteOrder.LITTLE_ENDIAN)
.putInt(string.length())
.array();
ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(string.getBytes());
gos.close();
os.close();
byte[] compressed = new byte[4 + os.toByteArray().length];
System.arraycopy(blockcopy, 0, compressed, 0, 4);
System.arraycopy(os.toByteArray(), 0, compressed, 4, os.toByteArray().length);
return compressed;
}
public static String decompress(byte[] compressed) throws IOException {
final int BUFFER_SIZE = 32;
ByteArrayInputStream is = new ByteArrayInputStream(compressed, 4, compressed.length - 4);
GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
StringBuilder string = new StringBuilder();
byte[] data = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = gis.read(data)) != -1) {
string.append(new String(data, 0, bytesRead));
}
gis.close();
is.close();
return string.toString();
}
동일한 테스트 스크립트를 사용할 수 있습니다.
압축 된 "Hello"가 무엇이든 BQAAAB + LC ...로 압축 된 것이 무엇이든 gzipper의 특히 열악한 구현입니다. deflate 형식의 정적 블록 대신 동적 블록을 사용하여 필요 이상으로 "Hello"를 확장했습니다. gzip 스트림 (항상 16 진수 1f 8b로 시작)에서 4 바이트 접두사를 제거한 후 "Hello"가 123 바이트로 확장되었습니다. 압축의 세계에서 그것은 범죄로 간주됩니다.
불평하는 압축 방법이 올 바르고 올바르게 작동하고 있습니다. 정적 블록을 생성하고 총 25 바이트의 출력을 생성합니다. gzip 형식에는 10 바이트 헤더와 8 바이트 트레일러 오버 헤드가 있으므로 5 바이트 입력은 7 바이트로 코딩되어 있습니다. 그게 더 비슷합니다.
압축 할 수없는 스트림은 확장되지만 많이 늘어나서는 안됩니다. gzip에서 사용하는 deflate 형식은 압축 할 수없는 데이터의 경우 16K ~ 64K마다 5 바이트를 추가합니다.
실제 압축을 얻으려면 일반적으로 압축 가능한 데이터에서 반복되는 문자열과 편향된 통계를 찾을 수 있도록 5 바이트로 작업 할 수 있도록 압축기에 훨씬 더 많은 것을 제공해야합니다. 나는 당신이 짧은 문자열로 테스트를하고 있었다는 것을 이해합니다. 그러나 실제 응용 프로그램에서는 문자열을 보내는 것이 항상 더 낫기 때문에 이러한 짧은 문자열로 범용 압축기를 사용하지 않을 것입니다.
귀하에 Decompress()
있어서, 64 기수의 처음 4 바이트로 전달하기 전에 스킵 입력을 디코딩 GZipInputStream
. 이러한 바이트는 05 00 00 00
이 특정 경우에 있습니다. 따라서 Compress()
메서드에서 이러한 바이트는 Base64 인코딩 직전에 다시 넣어야합니다.
이렇게하면 Compress ()는 다음을 반환합니다.
BQAAAB+LCAAAAAAAAADLSM3JyQcAhqYQNgUAAAA=
나는 이것이 당신의 기대와 정확히 같지 않다는 것을 알고 있습니다.
BQAAAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyLmeVlW/w+GphA2BQAAAA==
그러나 내 결과가에 다시 연결되면 Decompress()
여전히 "Hello"
. 시도 해봐. 차이는 원래 문자열을 얻은 압축 수준이 다르기 때문일 수 있습니다.
그렇다면 신비한 접두사가 붙은 바이트는 05 00 00 00
무엇입니까? 이 답변 에 따르면 압축 된 문자열의 길이 일 수 있으므로 프로그램이 압축 해제 된 바이트 버퍼의 길이를 알 수 있습니다. 여전히이 경우에는 집계되지 않습니다.
다음은 compress ()의 수정 된 코드입니다.
public static String Compress(String text) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// TODO: Should be computed instead of being hard-coded
baos.write(new byte[]{0x05, 0, 0, 0}, 0, 4);
GZIPOutputStream gzos = new GZIPOutputStream(baos);
gzos.write(text.getBytes());
gzos.close();
return Base64.encode(baos.toByteArray());
}
최신 정보:
Android의 출력 문자열과 .NET 코드가 일치하지 않는 이유는 .NET GZip 구현이 더 빠른 압축 (따라서 더 큰 출력)을 수행하기 때문입니다. 원시 Base64 디코딩 된 바이트 값을 살펴보면 확실히 확인할 수 있습니다.
.그물:
1F8B 0800 0000 0000 04 00 EDBD 0760 1C49 9625262F 6DCA 7B7F 4AF5 4AD7 E074 A108 8060 1324 D890 4010 ECC1 88CD E692 EC1D 6947 2329 AB2A 81CA 6556655D 6616 40CC ED9D BCF7 DE7B EFBD F7DE 7BEF BDF7 BA3B 9D4E 27F7 DFFF 3F5C 6664 016C F6CE 4ADA C99E 2180 AAC8 1F3F 7E7C 1F3F 22E6 7959 56FF 0F86 A610 3605 0000 00
내 Android 버전 :
1F8B 0800 0000 0000 00 00 CB48 CDC9 C907 0086 A610 3605 0000 00
이제 GZip File Format을 확인하면 .NET 및 Android 버전이 초기 헤더와 후행 CRC32 및 크기 필드에서 거의 동일하다는 것을 알 수 있습니다. 유일한 차이점은 아래 필드에 있습니다.
- .NET의 경우 XFL = 04 (압축기가 가장 빠른 알고리즘을 사용함) 인 반면 Android에서는 00입니다.
- 실제 압축 된 블록
따라서 XFL 필드에서 .NET 압축 알고리즘이 더 긴 출력을 생성한다는 것이 분명합니다.
실제로 이러한 원시 데이터 값으로 바이너리 파일을 만든 다음 gunzip을 사용하여 압축을 해제하면 .NET 및 Android 버전 모두 "hello" 와 정확히 동일한 출력 을 제공합니다.
따라서 다른 결과에 대해 신경 쓸 필요가 없습니다.
내 프로젝트에서 코드를 시도했고 Android의 압축 방법에서 인코딩 버그를 발견했습니다.
byte[] blockcopy = ByteBuffer
.allocate(4)
.order(java.nio.ByteOrder.LITTLE_ENDIAN)
.putInt(str.length())
.array();
ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(str.getBytes());
위 코드에서 u는 수정 된 인코딩을 사용하고 문자열 길이가 아닌 바이트 길이를 채워야합니다.
byte[] data = str.getBytes("UTF-8");
byte[] blockcopy = ByteBuffer
.allocate(4)
.order(java.nio.ByteOrder.LITTLE_ENDIAN)
.putInt(data.length)
.array();
ByteArrayOutputStream os = new ByteArrayOutputStream( data.length );
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write( data );
나는이 문제에 열광했다. 결국, 제 경우 (.Net 4)에서는 .Net 호환성을 위해 처음에이 추가 4 바이트를 추가 할 필요가 없었습니다.
다음과 같이 간단하게 작동합니다.
Android 압축 :
public static byte[] compress(String string) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(string.getBytes());
gos.close();
byte[] compressed = os.toByteArray();
os.close();
return compressed;
}
.Net 압축 해제
public static byte[] DecompressViD(byte[] gzip)
{
// Create a GZIP stream with decompression mode.
// ... Then create a buffer and write into while reading from the GZIP stream.
using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
{
const int size = 4096;
byte[] buffer = new byte[size];
using (MemoryStream memory = new MemoryStream())
{
int count = 0;
do
{
count = stream.Read(buffer, 0, size);
if (count > 0)
{
memory.Write(buffer, 0, count);
}
}
while (count > 0);
return memory.ToArray();
}
}
}
좋아, 기존 답변이 너무 많을 때 칭찬하는 것을 싫어하지만 불행히도 대부분은 다양한 이유로 잘못되었습니다.
- .NET Framework 내의 GZIP 알고리즘에는 차이가 있습니다. .NET 4.5를 사용하는 경우 다른 답변에서 보는 대부분의 불만은 단순히 귀하에게 적용되지 않습니다 (2.0 또는 3.5를 사용하는 사람들에게 적용되지 않음). "고정 된"버전의 코드를 사용하면 실제로 압축 / 압축 해제를 망칠 수 있습니다.
- Java는 부호없는 byte []를 사용하고 .NET은 부호있는 byte []를 사용합니다. 이로 인해 해당 byte []를 얼마나 정확하게 전송하는지에 따라 전송 중에 문제가 발생할 수 있습니다.
- 더 많은 문제를 일으킬 수있는 byte []를 전송하기 위해 Base64를 사용했습니다. 다른 여러 가지 이유가 있지만 더 이상 징징 거리는 것을 건너 뛰고 코드를 살펴 보겠습니다.
.NET Framework 4.5를 사용하는 경우 여기에 필요한 C # 클래스가 있습니다 (추가로 Base64).
public class CompressString
{
private 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());
}
}
// Base64
public static string ZipBase64(string compress)
{
var bytes = Zip(compress);
var encoded = Convert.ToBase64String(bytes, Base64FormattingOptions.None);
return encoded;
}
public static string UnzipBase64(string compressRequest)
{
var bytes = Convert.FromBase64String(compressRequest);
var unziped = Unzip(bytes);
return unziped;
}
// Testing
public static bool TestZip(String stringToTest)
{
byte[] compressed = Zip(stringToTest);
Debug.WriteLine("Compressed to " + compressed.Length + " bytes");
String decompressed = Unzip(compressed);
Debug.WriteLine("Decompressed to: " + decompressed);
return stringToTest == decompressed;
}
}
다음은 필요한 Android / Java 클래스입니다.
public class CompressString {
public static byte[] compress(String string) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(string.getBytes());
gos.close();
byte[] compressed = os.toByteArray();
os.close();
return compressed;
} catch (IOException ex) {
return null;
}
}
public static String decompress(byte[] compressed) {
try {
final int BUFFER_SIZE = 32;
ByteArrayInputStream is = new ByteArrayInputStream(compressed);
GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
StringBuilder string = new StringBuilder();
byte[] data = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = gis.read(data)) != -1) {
string.append(new String(data, 0, bytesRead));
}
gis.close();
is.close();
return string.toString();
} catch (IOException ex) {
return null;
}
}
// Base64
public static String compressBase64(String strToCompress) {
byte[] compressed = compress(strToCompress);
String encoded = android.util.Base64.encodeToString(compressed, android.util.Base64.NO_WRAP);
return encoded;
}
public static String decompressBase64(String strEncoded) {
byte[] decoded = android.util.Base64.decode(strEncoded, android.util.Base64.NO_WRAP);
String decompressed = decompress(decoded);
return decompressed;
}
// test
public static boolean testCompression(String stringToTest) {
byte[] compressed = compress(stringToTest);
Log.d("compress-test", "Compressed to " + compressed.length + " bytes");
String decompressed = decompress(compressed);
Log.d("compress-test", "Decompressed to " + decompressed);
return stringToTest == decompressed;
}
}
그래서, 거기에 의존하지 않고 100 % 작동하는 압축 Android / Java / C # /. NET 클래스가 있습니다. .NET 4.5에서 작동하지 않는 문자열을 찾으면 ( "Hello world"에서 1000 단어 단편 이야기까지 모든 것을 시도했습니다)-알려주십시오.
Android 메서드 압축 해제가 정상이 아닙니다.
Android Compress-> 확인 :
public static byte[] compress(String string) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(string.getBytes());
gos.close();
byte[] compressed = os.toByteArray();
os.close();
return compressed;
}
.Net 압축 해제-> 확인 :
public static byte[] DecompressViD(byte[] gzip)
{
// Create a GZIP stream with decompression mode.
// ... Then create a buffer and write into while reading from the GZIP stream.
using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
{
const int size = 4096;
byte[] buffer = new byte[size];
using (MemoryStream memory = new MemoryStream())
{
int count = 0;
do
{
count = stream.Read(buffer, 0, size);
if (count > 0)
{
memory.Write(buffer, 0, count);
}
}
while (count > 0);
return memory.ToArray();
}
}
}
.Net 압축-> 확인 :
public static string compress(string text)
{
byte[] buffer = Encoding.UTF8.GetBytes(text);
MemoryStream ms = new MemoryStream();
using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
{
zip.Write(buffer, 0, buffer.Length);
}
ms.Position = 0;
MemoryStream outStream = new MemoryStream();
byte[] compressed = new byte[ms.Length];
ms.Read(compressed, 0, compressed.Length);
return Convert.ToBase64String(compressed);
}
Android 압축 풀기-> 비정상 :
public static String decompress(String zipText) throws IOException {
byte[] compressed = Base64.decode(zipText);
GZIPInputStream os = new GZIPInputStream(new ByteArrayInputStream(compressed));
GZIPInputStream gzipInputStream = new GZIPInputStream(os);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int value = 0; value != -1;) {
value = gzipInputStream.read();
if (value != -1) {
baos.write(value);
}
}
gzipInputStream.close();
baos.close();
return new String(baos.toByteArray(), "UTF-8");
}
다음은 시작하는 간단한 예입니다.
public static void main(String[] args) throws IOException
{
byte[] buffer = new byte[4096];
StringBuilder sb = new StringBuilder();
//read file to compress
String read = readFile( "spanish.xml", Charset.defaultCharset());
if( read != null )
{
//compress file to output
FileOutputStream fos = new FileOutputStream("spanish-new.xml");
GZIPOutputStream gzos = new GZIPOutputStream(fos);
gzos.write( read.getBytes());
gzos.close();
//uncompress and read back
FileInputStream fis = new FileInputStream("spanish-new.xml");
GZIPInputStream gzis = new GZIPInputStream(fis);
int bytes = 0;
while ((bytes = gzis.read(buffer)) != -1) {
sb.append( new String( buffer ) );
}
}
}
static String readFile(String path, Charset encoding) throws IOException {
byte[] encoded = Files.readAllBytes(Paths.get(path));
return new String(encoded, encoding);
}
Vb.net에서 그렇게합니다.
Public Function zipString(ByVal Text As String) As String
Dim res As String = ""
Try
Dim buffer As Byte() = System.Text.Encoding.UTF8.GetBytes(Text)
Dim ms As New MemoryStream()
Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, True)
zipStream.Write(buffer, 0, buffer.Length)
End Using
ms.Position = 0
Dim outStream As New MemoryStream()
Dim compressed As Byte() = New Byte(ms.Length - 1) {}
ms.Read(compressed, 0, compressed.Length)
Dim gzBuffer As Byte() = New Byte(compressed.Length + 3) {}
System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length)
System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4)
res = Convert.ToBase64String(gzBuffer)
Catch ex As Exception
Log("mdl.zipString: " & ex.Message)
End Try
Return res
End Function
Public Function unzipString(ByVal compressedText As String) As String
Dim res As String = ""
Try
Dim gzBuffer As Byte() = Convert.FromBase64String(compressedText)
Using ms As New MemoryStream()
Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)
ms.Write(gzBuffer, 4, gzBuffer.Length - 4)
Dim buffer As Byte() = New Byte(msgLength - 1) {}
ms.Position = 0
Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
zipStream.Read(buffer, 0, buffer.Length)
End Using
res = System.Text.Encoding.UTF8.GetString(buffer, 0, buffer.Length)
End Using
Catch ex As Exception
Log("mdl.unzipString: " & ex.Message)
End Try
Return res
End Function
'Programing' 카테고리의 다른 글
Java에서 서명되지 않은 짧은 (0) | 2020.12.30 |
---|---|
Core Data를 사용하고있는 지금 모델을 단위 테스트하는 방법은 무엇입니까? (0) | 2020.12.30 |
PHP의 트레이 트 내에서 클래스 생성자를 오버로드하는 방법> = 5.4 (0) | 2020.12.30 |
안드로이드의 이해 (0) | 2020.12.30 |
Rust에서 시스템 명령을 호출하고 출력을 캡처하려면 어떻게해야합니까? (0) | 2020.12.30 |