2

Java で文字列を圧縮する方法の例を探しました。

圧縮してから解凍する機能があります。圧縮はうまくいくようです:

   public static String encStage1(String str)
   {
      String format1 = "ISO-8859-1";
      String format2 = "UTF-8";
      if (str == null || str.length() == 0)
      {
         return str;
      }
      System.out.println("String length : " + str.length());
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      String outStr = null;
      try
      {
         GZIPOutputStream gzip = new GZIPOutputStream(out);
         gzip.write(str.getBytes());
         gzip.close();
         outStr = out.toString(format2);
         System.out.println("Output String lenght : " + outStr.length());
      } catch (Exception e)
      {
                  e.printStackTrace();

      }
      return outStr;
   }

しかし逆に、encStage1 からの戻り値を decStage3 に直接渡しても、文字列が GZIP 形式ではないという不平を言っています。

   public static String decStage3(String str)
   {
      if (str == null || str.length() == 0)
      {
         return str;
      }
      System.out.println("Input String length : " + str.length());
      String outStr = "";
      try
      {
         String format1 = "ISO-8859-1";
         String format2 = "UTF-8";
         GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(str.getBytes(format2)));
         BufferedReader bf = new BufferedReader(new InputStreamReader(gis, format2));
         String line;
         while ((line = bf.readLine()) != null)
         {
            outStr += line;
         }
         System.out.println("Output String lenght : " + outStr.length());
      } catch (Exception e)
      {
         e.printStackTrace();
      }
      return outStr;
   }

encStage1 から文字列を返すと、次のエラーが発生します。

   public String encIDData(String idData)
   {
      String tst = "A simple test string";
      System.out.println("Enc 0: " + tst);
      String stg1 = encStage1(tst);
      System.out.println("Enc 1: " + toHex(stg1));
      String dec1 = decStage3(stg1);
      System.out.println("unzip: " + toHex(dec1));
   }

出力/エラー:

Enc 0: A simple test string
String length : 20
Output String lenght : 40
Enc 1: 1fefbfbd0800000000000000735428efbfbdefbfbd2defbfbd495528492d2e51282e29efbfbdefbfbd4b07005aefbfbd21efbfbd14000000
Input String length : 40
java.io.IOException: Not in GZIP format
    at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:137)
    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:58)
    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:68)
4

2 に答える 2

7

小さなエラーは次のとおりです。

     gzip.write(str.getBytes());

デフォルトのプラットフォーム エンコーディングを使用しますが、Windows では ISO-8859-1 にはなりません。より良い:

     gzip.write(str.getBytes(format1));

「ISO-8859-1」、Latin-1 の代わりに、「Cp1252」、Windows Latin-1 (一部のヨーロッパ言語用) を使用することを検討できます。これにより、引用符などのコンマが追加されます。

主なエラーは、圧縮されたバイトを文字列に変換することです。Java はバイナリ データ (byte[]、InputStream、OutputStream) をテキスト (String、char、Reader、Writer) から分離します。テキストは常に内部的に Unicode で保持されます。バイト シーケンスは有効な UTF-8 である必要はありません。バイトを 1 バイト エンコーディング (ISO-8859-1 など) に変換することで回避できる場合があります。

最善の方法は

     gzip.write(str.getBytes(StandardCharsets.UTF_8));

したがって、完全な Unicode があり、すべてのスクリプトを組み合わせることができます。

および に解凍しByteArrayOutputStreamますnew String(baos.toByteArray(), StandardCharsets.UTF_8)。UTF-8 の InputStreamReader で BufferedReader を使用することも問題ありませんが、readLine は改行文字を破棄します

outStr += line + "\r\n"; // Or so.

明確な答え:

public static byte[] encStage1(String str) throws IOException
{
   try (ByteArrayOutputStream out = new ByteArrayOutputStream())
   {
       try (GZIPOutputStream gzip = new GZIPOutputStream(out))
       {
           gzip.write(str.getBytes(StandardCharsets.UTF_8));
       }
       return out.toByteArray();
       //return out.toString(StandardCharsets.ISO_8859_1);
       // Some single byte encoding
   }
}

public static String decStage3(byte[] str) throws IOException
{
   ByteArrayOutputStream baos = new ByteArrayOutputStream();
   try (GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(str)))
   {
       int b;
       while ((b = gis.read()) != -1) {
           baos.write((byte) b);
       }
   }
   return new String(baos.toByteArray(), StandardCharset.UTF_8);
}
于 2014-08-14T14:30:55.760 に答える
3

エンコード/デコードに toString/getBytes を使用するのは間違った方法です。この目的のために、BASE64エンコーディングのようなものを使用してみてください(jdk 1.8のjava.util.Base64)

証拠として、次の簡単なテストを試してください。

import org.testng.annotations.Test;
import java.io.ByteArrayOutputStream;
import static org.testng.Assert.assertEquals;

public class SimpleTest {

    @Test
    public void test() throws Exception {

        final String CS = "utf-8";

        byte[] b0 = {(byte) 0xff};
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        out.write(b0);
        out.close();

        byte[] b1 = out.toString(CS).getBytes(CS);

        assertEquals(b0, b1);
    }
}
于 2014-08-14T14:37:05.277 に答える