質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.53%

Javaを用いたFlatedecode(Zip形式)圧縮されたバイナリデータの解凍

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 3,066

tktk123

score 9

前提・実現したいこと

PDFファイルからの文字データ読み出しを作成していたのですが、"stream"~"endstream"で囲まれたバイナリデータ部分がFlateDecodeというZIP形式で圧縮されていて読むことが出来ません。
そこでバイナリデータ部のみを切り出したテキストファイルからバイナリデータを読み込みjava.util.zip.Inflaterを用いて解凍するプログラムを作成しているのですが、一通り組み終わり実行したところエラーメッセージがでてうまく進めることが出来ない状態です。

発生している問題・エラーメッセージ

Exception in thread "main" java.util.zip.DataFormatException: invalid code lengths set
    at java.util.zip.Inflater.inflateBytes(Native Method)
    at java.util.zip.Inflater.inflate(Inflater.java:259)
    at java.util.zip.Inflater.inflate(Inflater.java:280)
    at ProcessFlateDecode.ProcessFlateDecode.main(ProcessFlateDecode.java:37)

該当のソースコード

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.zip.Inflater;

class ProcessFlateDecode {

    public static void main(String[] args) throws Exception{

        ArrayList<Integer> list = new ArrayList<Integer>();

        FileInputStream ins = new FileInputStream("file.txt");

        while(true){
            int data = ins.read();
            if(data==-1) break;

            list.add( data );
        }
        ins.close();


        int compressedDataLength = list.size();
        byte[] output = new byte[compressedDataLength];
        for(int i=0; i<list.size(); i++){
            Integer o=(Integer)list.get(i);
            output[i]=(byte)(o.intValue());
        }

        Inflater decompresser = new Inflater();
        decompresser.setInput(output, 0, compressedDataLength);
        byte[] result = new byte[100];
        int resultLength = decompresser.inflate(result);

        decompresser.end();

        String outputString = new String(result, 0, resultLength, "UTF-8");
        System.out.println(outputString);
    }

}

補足情報(言語/FW/ツール等のバージョンなど)

実行環境としてはeclipse4.6Neonを利用しています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

圧縮されたデータをPDFから切り出してファイルへ出力するところで文字化けしているということはないでしょうか?その部分の処理が提示されてないので単なる推測です。

蛇足1:
バイナリーデータを読み込む処理に不自然に思える点があります。

  1. 入力ファイル名の拡張子が.txt
    直接論理には関係ありませんがバイナリーデータを格納するファイルに.txtという拡張子は不自然と思います。
  2. 入力ファイルの読み込み方
    ファイルの中身はInflater#setInputでbyte配列として渡すことになります。よって入力ストリームからbyte配列へ直接readする方が自然です。ファイルに格納されている全バイト数が必要ならファイルのサイズを求めるメソッドで直接求められます。以上からList<Integer>への読み込みは冗長といえます。また他の理由で一旦全データを読み込む必要があるならList<Integer>ではなくByteBufferなどのクラスを使った方が自然に思えます。

蛇足2:
上げておられる圧縮解除論理は「とりあえず試しにコーディングしてみた」というレベルかも知れません。もしそうならいいのですが、最終実装だとするとInflater関連の実装が中途半端であると思います。UTF-8エンコードされた100バイト以上の文字列が存在すると破綻するからです。本来はinflate/setInputを適切に呼び出しながら全部のデータの解凍が完了するまでループするような制御が必要だと思います。

蛇足3:
Inflaterの代わりにInflaterInputStreamを使うと圧縮データの入力ストリームを圧縮解除した入力ストリームへ簡単に変換できます。そのほうが楽かも知れません。


質問者さんからのコメントにより追記(11/7 19:04):

バイナリーデータ部分をテキストエディタで読み込んでいたとのことでしたので、やはり文字化けが原因と思います。一般にバイナリーデータはいったん文字へ変換すると(※)と元のデータは復元できません。それはバイナリーデータと文字データとの間の変換が通常は不可逆なためです。

PDFをJavaで扱うためには入力にバイナリー入力ストリーム(InputStream)を使い、文字であることがわかっている部分のみをStringへ変換するといった配慮が必要になります。文字とバイナリーが混在したデータを扱う処理は少々難易度が高く「こうするとよい」といった簡単なアドバイスが自分にはできません。PDFの中身を解析する目的自体には専用のライブラリーを使う方がよいかも知れません。「java PDFReader free」などで検索するといくつかあるようです。

※:文字へ変換
Java上では文字列ストリームを使ったり、Stringに変換したりすることにあたります。またテキストエディタで開くといったことも同様です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/07 17:54

    恥ずかしながらテキストエディタで開いたものを切り取っていたので文字化けしたものを入力ファイルとして使っていました。。。
    そこでPDFのバイナリデータのみを抽出するプログラムを書いていたのですが文字化けした形でなく、その部を進数に直した形でバイト列に入れてバイナリファイルとして保存すればよいのでしょうか?初歩的な質問で申し訳ありません。
    また回答していただいたのに遅れてしまい申し訳ありません。

    キャンセル

  • 2016/11/07 19:06

    そういうことでしたか。ここに記述しずらいので本文の方へ追記しておきました。

    キャンセル

  • 2016/12/13 10:28

    わかりやすくご丁寧にありがとうございました。なんとか参考にさせてもらい完成することができました。

    キャンセル

0

PDFのFlateDecodeについては、yamagu.exblog.jp|
PDFの仕様書を読む 第15回 3.2.7 Stream Objects - Standard Filters
 が非常に役立つと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.53%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる