20210106のJavaに関する記事は8件です。

Javaでラッパークラスは使うべきではない3つの理由

今までなんとなくでやっていたのですが、変数の指定の際にラッパークラスのプリミティブ型の違いがやっとわかったのでメモとして残しておきます。

理由1: ラッパークラスを使うとプログラムのパフォーマンスが悪くなるらしい
理由2: ラッパークラスはnullを許容してしまうため、行動を読む人に「nullが入ってしまう可能性がある」と誤解させてしまう可能性がある
理由3: ラッパークラスはオブジェクトの1つであるため、値同士を比較演算子で以下のように比較しようとした場合、falseになってしまう。
スクリーンショット 2021-01-06 21.52.00.png

またラッパークラスはメリットとしてメソッドが使える点が挙げられるが、それらはスタティックメソッドであるため、変数宣言にラッパークラスを用いなくてもメソッドを使いたいときだけそれらを呼び出せば良いので、基本は変数宣言もプリミティブ型で問題ない。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】JavaBeansとは

プログラミング勉強日記

2021年1月6日
今日初めてJavaBeansについて知ったので、JavaBeansとはなにか簡単にまとめる。

JavaBeansとは

 簡単に言うと、データを保存しておくための倉庫みたいなもの。Javaで書かれた再利用できるソフトウエアコンポーネント、その技術仕様のことをいう。ソフトウエアコンポーネントはクラスのことで、JavaBeansはWebアプリでデータを操作するときによく使われる。

JavaBeansの仕様

  • クラスをpublicで指定する
  • プロパティはprivateで指定する
  • public指定で引数なしのコンストラクタを定義する
  • プロパティにアクセスするためのgetter、setterメソッドを用意する
  • パッケージ化する
  • クラス名の最後は慣例的にBeanにする
  • 必須ではないが、java.io.Serializableインターフェースを実装する

参考文献

JavaBeansを使うメリット

 データの糸氏に書きかえを防ぐことができる。各データ属性はprivateなので外部からアクセスすることができず、必ずインスタンスを生成してからセッターを呼ばないとデータ属性を書きかえられない仕組みになっている。(このように外部からデータ属性を保護することをカプセル化という)

JavaBeansの注意点

 セッターを誤って使うと簡単にカプセル化が壊れてしまう。なので、セッターを用いる際には必ず正しい値が設定されているのか注意する必要がある。

参考文献

JavaBeansとは?仕様や使い方、永続化について解説
JavaBeansの使用方法を現役エンジニアが解説【初心者向け】

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】String型文字列の比較

はじめまして。Javaを学習して3ヶ月あまりのド初心者です!!w

現在はJava+HTML/CSSとJavaScriptを学習しております。日々知識が増えることが嬉しく、何よりもコーディングしているときよりも調べているときが無になれ、好きな時間です。
これから調べた事など、他の方と重複することだらけですが、アウトプットの練習も兼ねて投稿させてください!!

また間違えている事などあれば、ご指摘ください!!宜しくお願いいたします:sunny:

で今回記事にさせていただくのは、String型変数の比較についてです。
Javaでは

if(条件式){処理したい内容}

で比較を行えます。

int型ですと、

int a = 2;
int b = 2;
if(a == b ){
処理したい内容
}

で簡易に変数の比較が行えます。
上記の内容だと、当たり前にtrueの結果となります。

これをString型でも行うと、、、、

String a = "コロナどっかいけ”;
String b = "コロナどっかいけ”;
if( a == b ){
 処理したい内容
}

これはtrueの結果となります。
*本来はfalseですが、Javaによって判断してくれている。(メモリの有効性を考えて??)

では記入を増やして、、、

まずはint型

int a = 2;
int b = 2;
int c = a + 5;
int d = b + 5;
if(c == d ){
 System.out.println("等しい");
} else{
 System.out.println("等しくない");
}

この内容は、値は両者値は7なのでtrueですね。

では本題のString型では、、、、

 String a = "コロナどっかいけ";
 String b = "コロナどっかいけ";
 String c = a + "!!!";
 String d = b + "!!!";
 if(c == d ){
  System.out.println("内容は同一");
 } else{
  System.out.println("内容は不一致");
 }

この内容ではどうでしょうか??
実行してみると、、、、不一致が出力されます。
cとdの変数という箱を人間的にみると、"コロナどっかいけ!!!"なので同じだろうと思い込んでしまいます。
同じ"コロナどっかいけ”なのになぜ????という疑問が初学者には湧くかと思われます。
これは初学者のよくある落とし穴です。

String型の比較においては等値なのか、等価なのかで考えてみましょう。

等値とは『同じ値』
等価とは『同じ内容』
です。

 String a = "コロナどっかいけ";
 String b = "コロナどっかいけ";
 String c = a + "!!!";
 String d = b + "!!!";
 if(c == d ){
  System.out.println("内容は同一");
 } else{
  System.out.println("内容は不一致");
 }

においてはcとdそれぞれの変数という箱をあけてみると『同じ内容』であっても、同じ値ではありません。

これまで使用してきた『==』は同じ値を比較するものです。

また別の言い方をすると参照とはメモリ上の領域を確保しておく事です。
上記の比較だと、内容ではなく、領域の比較(同じ場所かどうか)ということになり
falseが返ってくるわけです。

では、このように参照型でも内容の比較を行たい場合はどのようにしたらよいのでしょうか??
ここで等価(『同じ内容』)が登場します。

文字列において、等価の比較はequals()メソッドを使用します。
使用方法は以下の通りです。

String a = "コロナどっかいけ";
String b = "コロナどっかいけ";
String c = a + "!!!";
String d = b + "!!!";
if(c.equals(d)){
   System.out.println("内容は同一");
} else{
   System.out.println("内容は不一致");
}

このようにequals()メソッドを使用すれば、参照型文字列の比較を行えます。
これから学ぶであろう、インスタンスなどで別のインスタンスの同じ文字列を比較するなどするときも、このequals()メソッドを使用しないと思ってる返答が得られないと思いますので、
ぜひ文字列の比較はequals()メソッドと覚えておきましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでSpring Boot環境構築(2021年1月版)

Spring Bootを触れる環境を急遽準備する必要ができたので、作成したときの手順とかちょっとした考え事まとめ。

0.参考記事

Docker × Spring Boot環境構築
https://qiita.com/A-Kira/items/beaf79a0d39d9839e61e

ほとんどこの通りに実行しようとして、時間経過による変更値があったので、その周りを書き足した程度の提灯記事です。並行して読むほうが良いと思います。

1.筆者環境

  • Windows 10 Professional(20h4)
  • VSCode
  • Git Bash
  • Docker Desktop

このあたりの詳細については割愛します(元記事はMacだったのですがまぁ問題ないでしょう)

2.自力で作成するファイルを作成

自動生成されるファイルが多いので、元記事を端折って手動で編集するファイルだけ取り上げます。

docker-compose.yml

docker-compose.yml
version: '3.8'
services:
  java:
    image: openjdk:15-slim
    ports:
      - 8080:8080
    tty: true
    volumes:
      - ./server:/srv:cached
    working_dir: /srv

先にさっくり書いてしまうと、元記事との違いは
- versionを3.6→3.8(ここはほぼ影響なし)
- imageのバージョンを14→15(大事)

後述しますが、initializrのバージョンが14から15に変わっているので、ここを元記事のままで続けるとgradleがコケます。

一応筆者個人としては、dockerhubに"openjdk:15-slim"があることを確認しての記載をしましたよという、ぶっ放し(格ゲー文脈)で書き換えたわけではないという申し訳程度のフォローを入れておきます。
https://hub.docker.com/r/amd64/openjdk/

Gradleプロジェクトファイル

元記事と同様、spring initializrで作成し、【プロジェクトディレクトリ】/server下に展開します。

ただし、バージョン周りが当時と変わっているので、その違いのみ。
image.png

  • (先に書いてますが)Javaの選択地に14がなく、最新は15になっている
  • Spring Bootの選択に2.3.1がなくなっている(本手順では2.4.1を選択)

Dependenciesについては、元記事同様"Spring Web"と"Lombok"を選択しました。

プロジェクトを試験動作させるための変更

元記事と同様ですが一応。server/src/main/java/com/example/api/ApiApplication.javaを以下のように編集。

ApiApplication.java
package com.example.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class ApiApplication {

    @RequestMapping("/")
    public String home() {
        return "Hello World";
    }

    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }

}

3.Docker起動

全く同じです。

コマンドラインから、Docker環境をビルドして

% docker-compose build

バックグラウンドでDockerを起動

% docker-compose up -d

4. Gradleのビルド

ここも全く同じです。

Javaコンテナ内に入る ※人によってwinptyは必要だったり必要なかったり

% winpty docker-compose exec java bash

gradleビルド(root@以降は各人違います。一応)

root@558036f3cbd0:/srv# sh gradlew build

先程も書きましたが、コンテナのJavaを15にしておかないと、以下のようにFailureとか言われる羽目になります(1敗)。

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> Could not target platform: 'Java SE 15' using tool chain: 'JDK 14 (14)'.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug
option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 53s
1 actionable task: 1 executed

5.実行と動作確認

ここも全く同じ

root@558036f3cbd0:/srv# java -jar build/libs/api-0.0.1-SNAPSHOT.jar

localhost:8080にアクセスすると、先程書いたhome()が実行されて"Hello World"が表示されているはずです。

99.感想

この記事が世間的に必要かどうかはさておき、今回の程度のバージョン差異は数回コケながら自力でさっさと解決できるくらいが程々のエンジニア力だと思います(こなみかん

とはいえ想定通りにコケるとか、コケるだろうなと予想しつつ未修正のまま突っ込んで期待通りの結果を得るというのも個人的には大事だと思うので、未来にこれと同じような事態を踏んだ人は同じように試したりすると良いと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JNA で クリップボードを監視

JNA で Windows の clipbord を監視したいので、いろいろ調べました。
参考になったサイト:
https://stackoverflow.com/a/28693513
https://java-native-access.github.io/jna/4.2.0/com/sun/jna/platform/win32/WinUser.WindowProc.html
https://www.codota.com/code/java/methods/com.sun.jna.platform.win32.User32/DefWindowProc
http://kaitei.net/winapi/window-procedures/
https://github.com/EsotericSoftware/clippy/blob/master/src/com/esotericsoftware/clippy/Win.java
https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
https://www.cnblogs.com/tangchun/p/9288626.html
https://blog.csdn.net/tcjiaan/article/details/8951280
https://www.cnblogs.com/iszero/p/4020207.html
https://reehappy.com/java009/

大まかの流れ:

  1. 見えない Window を作り、Window の lpfnWndProc に WindowProc を設定。
  2. User32.AddClipboardFormatListener を呼び出し、クリップボードが変化時に Message が飛んでくるようになる。
  3. WM_CLIPBOARDUPDATE をチャッチ。
  4. 終了時に、RemoveClipboardFormatListener を呼び出し、Listener 登録解除。

サンプルコード。jna-5.5.0.jar で検証

MyUser32.java
package clipbord;

import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.win32.W32APIOptions;

final public class MyUser32 {

    static {
        Native.register(NativeLibrary.getInstance("user32", W32APIOptions.DEFAULT_OPTIONS));
    }

    static public final int WM_CLIPBOARDUPDATE = 0x31D;

    // https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
    static public final int CF_TEXT = 1;
    static public final int CF_UNICODETEXT = 13;
    static public final int CF_HDROP = 15;

    // Clipboard
    static public native boolean AddClipboardFormatListener(Pointer hWnd);
    static public native boolean RemoveClipboardFormatListener(Pointer hWnd);
    static public native boolean OpenClipboard(Pointer hWnd);
    static public native boolean CloseClipboard(Pointer hWnd);
    static public native boolean EmptyClipboard();
    static public native boolean IsClipboardFormatAvailable(int format);
    static public native Pointer GetClipboardData(int format);
    static public native Pointer SetClipboardData(int format, Pointer hMem);
    static public native Pointer GetClipboardOwner();
}
ClipboardWatcher.java
package clipbord;

import java.util.Scanner;

import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.WNDCLASSEX;
import com.sun.jna.platform.win32.WinUser.WindowProc;

public class ClipboardWatcher {

    public static void main(String[] args) {
        new ClipboardWatcher().start();
    }

    private Thread thread;
    private HWND _hWnd;

    final public void start() {
        this.thread = new Thread(this::myThread);
        this.thread.start();
        {
            System.out.println("watching clipboard. press any key to exit...");
            Scanner sc = new Scanner(System.in);
            sc.next();
            sc.close();
        }
        this.stop();
    }

    final public void stop() {
        boolean remove = MyUser32.RemoveClipboardFormatListener(this._hWnd.getPointer());
        System.out.println("RemoveClipboardFormatListener:" + remove);
        User32.INSTANCE.PostMessage(this._hWnd, User32.WM_QUIT, null, null);
        System.out.println("exit");
    }

    private final WindowProc callback = new WindowProc() {
        @Override
        public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam) {
            //System.out.println(uMsg);
            switch (uMsg) {
            case WinUser.WM_CREATE:
                System.out.println("WM_CREATE");
                return new LRESULT(0);

            case WinUser.WM_DESTROY:
                System.out.println("WM_DESTROY");
                User32.INSTANCE.PostQuitMessage(0);
                return new LRESULT(0);

            case MyUser32.WM_CLIPBOARDUPDATE:
                // System.out.println("WM_CLIPBOARDUPDATE");
                MyUser32.OpenClipboard(hWnd.getPointer());
                if (/*MyUser32.IsClipboardFormatAvailable(MyUser32.CF_TEXT) ||*/ MyUser32.IsClipboardFormatAvailable(MyUser32.CF_UNICODETEXT)) {
                    /* do something here */
                    System.out.println(MyUser32.GetClipboardData(MyUser32.CF_UNICODETEXT).getWideString(0));
                }
                MyUser32.CloseClipboard(hWnd.getPointer());
                return new LRESULT(0);

            default:
                return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
            }
        }
    };

    private void myThread() {
        //WString className = new WString("myclass");
        String className = "myclass";
        WNDCLASSEX wx = new WNDCLASSEX();
        wx.clear();
        wx.lpszClassName = className;
        wx.lpfnWndProc = callback;

        if (User32.INSTANCE.RegisterClassEx(wx).intValue() != 0) {
            this._hWnd = User32.INSTANCE.CreateWindowEx(0, className, null, 0, 0, 0, 0, 0, null, null, null, null);
            boolean add = MyUser32.AddClipboardFormatListener(this._hWnd.getPointer());
            System.out.println("AddClipboardFormatListener:" + add);

            WinUser.MSG msg = new WinUser.MSG();
            msg.clear();

            while (User32.INSTANCE.GetMessage(msg, this._hWnd, 0, 0) > 0) {
                User32.INSTANCE.TranslateMessage(msg);
                User32.INSTANCE.DispatchMessage(msg);
            }
        }
    }
}

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java   Word文書で段落の文字色を変更

この記事ではWord文書で段落の文字色を変更する方法を紹介します。前のようにSpire.Doc for Javaというライブラリが必要になります。

下準備

1.E-iceblueの公式サイトからFree Spire.doc for Java無料版をダウンロードしてください。

f:id:lendoris:20210106114703p:plain

2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire.doc.jarを参照に追加してください。

f:id:lendoris:20210106114719p:plain

元のファイル

f:id:lendoris:20210106114800p:plain

コード

import com.spire.doc.*;
import com.spire.doc.documents.*;
import com.spire.doc.fields.TextRange;

import java.awt.*;

public class ChangeFontColor {
    public static void main(String[] args){
        //Document objectを作成します。
        Document doc = new Document();
        //Wordをロードします。
        doc.loadFromFile("FontColorExample.docx");

        //初めのsectionを取得します。
        Section section = doc.getSections().get(0);
        //初めのsectionで二つ目の段落をを取得します。
        Paragraph p1 = section.getParagraphs().get(1);

        //二つ目の段落をループします。
        for (int i = 0; i < p1.getChildObjects().getCount(); i ++)
        {
            //二つ目の段落のテキストの色を変更します。
            if ( p1.getChildObjects().get(i) instanceof TextRange)
            {
                TextRange tr = (TextRange) p1.getChildObjects().get(i);
                tr.getCharacterFormat().setTextColor(Color.green);
            }
        }

        //三つ目の段落を取得します。 
        Paragraph p2 = section.getParagraphs().get(2);

        //三つ目の段落をループします
        for (int j = 0; j < p2.getChildObjects().getCount(); j ++)
        {
            //三つ目の段落のテキストの色を変更します。
            if ( p2.getChildObjects().get(j) instanceof TextRange)
            {
                TextRange tr = (TextRange) p2.getChildObjects().get(j);
                tr.getCharacterFormat().setTextColor(Color.blue);
            }
        }

        //保存します。
        doc.saveToFile("ChangeFontColor.docx", FileFormat.Docx_2013);
    }
}

完成例

f:id:lendoris:20210106114855p:plain

 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java  PowerPointにウォーターマークを追加・削除

ウォーターマークとは静止画像や動画の盗用を防ぐため、著作権を表示する文字やロゴ、図のことです。今回はSpire.Presentation for Javaを利用してJavaでPowerPointに画像のウォーターマークと文字のウォーターマークを追加・削除する方法を紹介します。

下準備

1.E-iceblueの公式サイトからFree Spire. Presentation for Java無料版をダウンロードしてください。

f:id:lendoris:20210106113230p:plain

2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire. Presentation.jarを参照に追加してください。

 f:id:lendoris:20210106113242p:plain

元のファイル

f:id:lendoris:20210106113408p:plain

文字のウォーターマーク

import com.spire.presentation.*;
import com.spire.presentation.drawing.FillFormatType;
import java.awt.*;
import java.awt.geom.Rectangle2D;

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

        //presentation objectを作成します。 
        Presentation presentation = new Presentation();
        presentation.loadFromFile("C:\\Users\\Administrator\\Desktop\\Sample.pptx");

        //ウォーターマークの幅と長さを設定します。
        int width= 400;
        int height= 300;

        //長方形のフィールドを作成します。
        Rectangle2D.Double rect = new Rectangle2D.Double((presentation.getSlideSize().getSize().getWidth() - width) / 2,
                (presentation.getSlideSize().getSize().getHeight() - height) / 2, width, height);

        //長方形のフィールドにshapeを追加します。
        IAutoShape shape = presentation.getSlides().get(0).getShapes().appendShape(ShapeType.RECTANGLE, rect);

        /shapeのデザインを配置します
        shape.getFill().setFillType(FillFormatType.NONE);
        shape.getShapeStyle().getLineColor().setColor(Color.white);
        shape.setRotation(-45);
        shape.getLocking().setSelectionProtection(true);
        shape.getLine().setFillType(FillFormatType.NONE);

        //shapeにテキストを追加します。
        shape.getTextFrame().setText("複写禁止");
        PortionEx textRange = shape.getTextFrame().getTextRange();

        //ウォーターマークのデザインを配置します。
        textRange.getFill().setFillType(FillFormatType.SOLID);
        textRange.getFill().getSolidColor().setColor(Color.pink);
        textRange.setFontHeight(50);

        //保存します。
        presentation.saveToFile("output/result.pptx", FileFormat.PPTX_2010);
    }

}

完成例

f:id:lendoris:20210106113430p:plain

画像のウォーターマーク

import com.spire.presentation.*;
import com.spire.presentation.drawing.*;
import javax.imageio.ImageIO;
import java.io.File;


public class addImageWatermark {

    public static void main(String[] args) throws Exception {
        //文書をロードします。
        Presentation presentation = new Presentation();
        presentation.loadFromFile("Sample.pptx");

        //画像のウォーターマークを取得します。

        File file =new File("logo.png");
        IImageData image = presentation.getImages().append(ImageIO.read(file));

        // スライドの背景属性を取得し、画像の塗りつぶしを配置します。
 presentation.getSlides().get(0).getSlideBackground().setType(BackgroundType.CUSTOM);
        presentation.getSlides().get(0).getSlideBackground().getFill().setFillType(FillFormatType.PICTURE);
        presentation.getSlides().get(0).getSlideBackground().getFill().getPictureFill().setFillType(PictureFillType.STRETCH);
        presentation.getSlides().get(0).getSlideBackground().getFill().getPictureFill().getPicture().setEmbedImage(image);


        //保存します
        presentation.saveToFile("addImageWatermark.pptx";, FileFormat.PPTX_2013);
    }
}

完成例

f:id:lendoris:20210106113546p:plain

ウォーターマークを削除するコード

import com.spire.presentation.*;
import com.spire.presentation.drawing.*;

public class removeTextOrImageWatermark {

    public static void main(String[] args) throws Exception {
        //ドキュメントをロードします。
        Presentation presentation = new Presentation();
        presentation.loadFromFile("Sample.pptx");

        //文本のウォーターマークを削除します。
        for (int i = 0; i < presentation.getSlides().getCount(); i++)
        {
            for (int j = 0; j < presentation.getSlides().get(i).getShapes().getCount(); j++)
            {
                if (presentation.getSlides().get(i).getShapes().get(j) instanceof IAutoShape)
                {
                    IAutoShape shape = (IAutoShape)presentation.getSlides().get(i).getShapes().get(j);
                    if (shape.getTextFrame().getText().contains("E-iceblue"))
                    {
                        presentation.getSlides().get(i).getShapes().remove(shape);
                    }
                }
            }
        }

        //画像のウォーターマークを削除します。
        for (int i = 0; i < presentation.getSlides().getCount(); i++)
        {
            presentation.getSlides().get(i).getSlideBackground().getFill().setFillType(FillFormatType.NONE);
        }

        //保存します。
        presentation.saveToFile("removeTextOrImageWatermark.pptx";, FileFormat.PPTX_2013);
    }
}

 

 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaでbyte配列をgzip圧縮してファイル出力する

タイトルの処理を作成した際に試行錯誤したメモ。

とりあえず書いたソース

こんな感じでbyte配列をgzipにしてファイルに出力してました。
先に別用途でファイル出力する処理を書いていたのを改造した際に、あれこれ調べながら付け足し付け足ししてしまい、今思うと謎に冗長な書き方をしてます。

test.java
        byte[] bytes = output.toByteArray();
        // byte[]をgzip化
        String gzipStr = "";
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            GZIPOutputStream gzip = new GZIPOutputStream(out);
            gzip.write(bytes);
            gzip.close();
            gzipStr = out.toString();
            out.close();
        }catch (IOException e) {
            e.printStackTrace();
            return;
        }
        // gzip化したデータをファイル書き出し
        String file = "出力ファイルパス";
        try(PrintWriter writer = new PrintWriter(
            new BufferedWriter(
            new OutputStreamWriter(
            new FileOutputStream
              (file),"UTF-8")))) {
            writer.print(gzipStr);

        } catch (IOException e) {
            e.printStackTrace();
        }

エラー内容

↑のJavaをコンパイルして実行し、出力したファイルを解凍しようとすると下記のエラーが。

01-05 08:35:22.294 6117-6117/? W/System.err: java.io.IOException: unknown format (magic number ef1f)
01-05 08:35:22.294 6117-6117/? W/System.err:     at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:109)
01-05 08:35:22.294 6117-6117/? W/System.err:     at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:88)
 以下略

恥ずかしながら何もわからず、「gzip マジックナンバーとは」とかでググるところから。(マジックナンバー (フォーマット識別子) - wikipedia)

gzipはファイルの先頭が「1f8b」で始まり、これがマジックナンバー(形式を判断するための符号)なのですが、このファイルは「ef1f」から始まっているためファイル形式が判定できないよということみたいです。

バイナリファイルを確認

Stirlinで出力したファイルを確認してみると、確かに最初が「1f8b」ではないですね。(でも「ef1f」でなく「1fef」のような…)
無題.png

ソース修正

出力時にこねくり回しているのが悪いのかな、と調べて下記サイトを発見。
ANDROIDでGZIPファイルの圧縮・展開をする - TECHBOOSTER

えーこんなにシンプルに書いていいの、と驚きながら参考にさせていただきソースを修正したものがこちら:

test.java
        byte[] bytes = output.toByteArray();
        String file = "出力ファイルパス";
        try {
            GZIPOutputStream gzip = new GZIPOutputStream(new FileOutputStream(file));
            gzip.write(bytes);
            gzip.close();
        }catch (IOException e) {
            e.printStackTrace();
            return;
        }

これでファイル出力したら正常に解凍できました。

バイナリファイルも先頭が「1f8b」になりましたので、大丈夫そうです。
無題.png


諸事情によりIDEを使わずテキストエディタで書いたんですが、importだけですっかり気が滅入りました…JavaはIDEを使おう……

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む