web-dev-qa-db-ja.com

Javaで文字列を圧縮する方法は?

GZIPOutputStreamまたはZIPOutputStreamを使用して文字列を圧縮します(私のstring.length()は20未満です)が、圧縮結果は元の文字列より長くなります。

あるサイトでは、これは私の元の文字列が短すぎるため、GZIPOutputStreamを使用して長い文字列を圧縮できるためだと言っている友人がいます。

だから、誰かが文字列を圧縮するのに助けてくれますか?

私の機能は次のようなものです:

String compress(String original) throws Exception {

}

更新:

import Java.io.ByteArrayOutputStream;
import Java.io.IOException;
import Java.util.Zip.GZIPOutputStream;
import Java.util.Zip.*;


//ZipUtil 
public class ZipUtil {
    public static String compress(String str) {
        if (str == null || str.length() == 0) {
            return str;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip = new GZIPOutputStream(out);
        gzip.write(str.getBytes());
        gzip.close();
        return out.toString("ISO-8859-1");
    }

    public static void main(String[] args) throws IOException {
        String string = "admin";
        System.out.println("after compress:");
        System.out.println(ZipUtil.compress(string));
    }
}

結果は次のとおりです。

alt text

51
user421851

圧縮アルゴリズムは、ほとんどの場合、何らかの形式のスペースオーバーヘッドを持ちます。つまり、オーバーヘッドが保存スペースの量よりも小さくなるほど十分に大きいデータを圧縮する場合にのみ有効です。

わずか20文字の文字列を圧縮することは簡単ではなく、常に可能であるとは限りません。繰り返しがある場合、ハフマンコーディングまたは単純なランレングスエンコーディングで圧縮できる可能性がありますが、おそらくあまり圧縮できません。

38
JesperE

文字列を作成するとき、それは文字のリストと考えることができます。つまり、文字列の各文字について、可能なすべての文字の値をサポートする必要があります。太陽から docs

char:charデータ型は単一の16ビットUnicode文字です。最小値は '\ u0000'(または0)、最大値は '\ uffff'(または65,535を含む)です。

サポートする文字のセットが少ない場合は、単純な圧縮アルゴリズムを記述できます。これは、バイナリ-> 10進数-> 16進基数変換に似ています。 65,536(またはターゲットシステムがサポートする多くの文字)から26(アルファベット)/ 36(英数字)などになります。

タイムスタンプをテキストとしてエンコードするなど、このトリックを数回使用しました(ターゲット36 +、ソース10)-十分な単体テストがあることを確認してください!

9
Jon Freedman

パスワードが多かれ少なかれ「ランダム」である場合、運が悪ければ、サイズを大幅に削減することはできません。

しかし:なぜパスワードを圧縮する必要があるのですか?必要なのは圧縮ではなく、ある種のハッシュ値でしょうか?名前が特定のパスワードと一致するかどうかを確認するだけでよい場合は、パスワードを保存する必要はありませんが、パスワードのハッシュを保存できます。入力したパスワードが特定の名前と一致するかどうかを確認するには、同じ方法でハッシュ値を作成し、保存したハッシュと比較できます。ハッシュ(Object.hashCode())はintであるため、20個のパスワードハッシュすべてを80バイトで格納できます)。

8
Arne Deutsch

あなたの友達は正しいです。 gzipとZipはどちらも [〜#〜] deflate [〜#〜] に基づいています。これは汎用アルゴリズムであり、小さな文字列をエンコードするためのものではありません。

これが必要な場合、可能な解決策はカスタムエンコードとデコードHashMap<String, String>。これにより、単純な1対1のマッピングを行うことができます。

HashMap<String, String> toCompressed, toUncompressed;

String compressed = toCompressed.get(uncompressed);
// ...
String uncompressed = toUncompressed.get(compressed);

明らかに、これにはセットアップが必要であり、少数の文字列に対してのみ実用的です。

6

Zipアルゴリズムは、 [〜#〜] lzw [〜#〜]Huffman Trees の組み合わせです。これらのアルゴリズムのいずれかを個別に使用できます。

圧縮は2つの要因に基づいています:

  • 元のチェーン内の部分文字列の繰り返し(LZW):繰り返しが多い場合、圧縮が効率的になります。このアルゴリズムは、単語が頻繁に繰り返されるため、長いプレーンテキストの圧縮に優れたパフォーマンスを発揮します。
  • 圧縮チェーン内の各文字の数(ハフマン):文字間の再分割が不均衡になるほど、圧縮が効率的になります。

あなたの場合、LZWアルゴリズムのみを試してください。基本的には、メタ情報を追加せずにチェーンを圧縮できます。おそらく、短い文字列の圧縮に適しています。

ハフマンアルゴリズムの場合、圧縮されたテキストと共にコーディングツリーを送信する必要があります。そのため、小さなテキストの場合、結果はツリーのために元のテキストよりも大きくなる可能性があります。

4
Benoit Courtine

ここでは、ハフマンエンコーディングが賢明なオプションです。 Gzipと友人はこれを行いますが、彼らが働く方法は、入力用のハフマンツリーを構築し、それを送信し、ツリーでエンコードされたデータを送信することです。ツリーがデータに比べて大きい場合、サイズが節約されないことはありません。

ただし、ツリーの送信を回避することもできます。代わりに、送信者と受信者がすでにツリーを持っているように調整します。すべての文字列に対して具体的に構築することはできませんが、すべての文字列をエンコードするために使用される単一のグローバルツリーを持つことができます。入力文字列と同じ言語(英語など)からビルドする場合でも、すべての入力のカスタムツリーほどではないものの、優れた圧縮が得られるはずです。

4
Tom Anderson

Huffman Coding が役立つ場合がありますが、小さな文字列に頻繁に文字が多い場合のみ

4
Noel M

文字列の大部分がASCIIであることがわかっている場合は、それらをUTF-8に変換できます。

byte[] bytes = string.getBytes("UTF-8");

これにより、メモリサイズが約50%削減される場合があります。ただし、文字列ではなくバイト配列が出力されます。ただし、ファイルに書き込む場合は問題になりません。

文字列に戻すには:

private final Charset UTF8_CHARSET = Charset.forName("UTF-8");
...
String s = new String(bytes, UTF8_CHARSET);
2
rghome

ハフマンアルゴリズムを見てください。

https://codereview.stackexchange.com/questions/44473/huffman-code-implementation

これは、テキスト内の頻度に応じて、各文字がビットのシーケンスに置き換えられるというものです(頻度が高いほど、シーケンスは小さくなります)。

テキスト全体を読んで、コードの表を作成できます。次に例を示します。

シンボルコード

0

s 10

e 110

m 111

このアルゴリズムは、テキスト入力に基づいてシンボルツリーを構築します。文字の種類が多ければ多いほど、圧縮率は最悪になります。

ただし、テキストによっては効果的な場合があります。

0
live-love

Java 9 https://openjdk.Java.net/jeps/254 ですぐに使用できるコンパクトな文字列拡張機能

Java.lang.Stringには次が含まれます。

プライベート最終バイト[]値。

0
Anurag Sharma

GZIPOutputStreamまたはZIPOutputStreamを使用して実際の圧縮を行うには、少なくとも数百バイトを必要とするため、文字列の圧縮は発生しません。文字列が小さすぎます(同じ理由で圧縮が必要な理由がわかりません)

これから結論を確認してください 記事

また、この記事では、ネットワークトラフィックを削減し、クライアント/サーバーアプリケーションのパフォーマンスを向上させるために、オンザフライでデータを圧縮および圧縮解除する方法も示しています。ただし、オンザフライでデータを圧縮すると、圧縮されるオブジェクトが数百バイトを超える場合にのみ、クライアント/サーバーアプリケーションのパフォーマンスが向上します。たとえば、圧縮および転送されるオブジェクトが単純なStringオブジェクトである場合、パフォーマンスの向上を観察することはできません。

0
YoK