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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

20028閲覧

Java - Jar ファイルの中のリソースの読み込みについて nullを参照してしまう

FoolHotari

総合スコア79

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2017/02/28 14:35

編集2017/02/28 15:23

##目的
Java で Jar ファイルの中にリソースファイルを入れ、
そのリソースファイルを読み込みたいのですが、
その読み込むクラスと同じ階層のファイルしか読み込むことができません。

一つ下の階層のファイルまで読み込むにはどうすればよいのでしょか。

勉強は Eclipse を使っています。Eclipse で起動したときは下のようなエラーは発生しません。

###ファイル構成

![イメージ説明

Out.txt は起動後に生成されます。

###ファイルの内容

  • same.txt <= same
  • first.txt <= first
  • second.txt <= second
  • third.txt <= third

それぞれのファイル名が一文ずつ入力されています。

##エラーメッセージ

Exception in thread "main" java.lang.NullPointerException at java.io.Reader.<init>(Reader.java:78) at java.io.InputStreamReader.<init>(InputStreamReader.java:97) at step16.Test.<init>(Test.java:23) at step16.Test.main(Test.java:45)

生成した Jar ファイルをコマンドプロンプトで起動しました。
リソースファイルが見つからないようです。

###ソースコード

Java

1package step16; 2 3import java.io.*; 4 5public class Test { 6 Test(String name){ 7 try { 8 File f = new File("Out.txt"); 9 FileWriter fw = new FileWriter(f,true); 10 BufferedWriter bw = new BufferedWriter(fw); 11 PrintWriter pw = new PrintWriter(bw); 12 13 InputStream is = getClass().getResourceAsStream(name); 14 InputStreamReader isr = new InputStreamReader(is,"UTF-8"); 15 BufferedReader br = new BufferedReader(isr); 16 17 while (true){ 18 String temp = br.readLine(); 19 20 if (temp==null){ 21 break; 22 } 23 24 pw.println(temp); 25 } 26 27 pw.close(); 28 br.close(); 29 } catch (IOException e) { 30 } 31 } 32 @SuppressWarnings("unused") 33 public static void main(String[] args){ 34 Test t1=new Test("same.txt"); //同じ階層のファイル 35 Test t2=new Test("first\\first.txt"); //一つ下の階層のファイル 36 Test t3=new Test("../second.txt"); //一つ上の階層のファイル 37 Test t4=new Test("../third\\third.txt"); //一つ上の階層にあるディレクトリにあるファイル 38 } 39} 40

###実行結果

Eclipseで起動
Out.txt の内容は

  1. same
  2. first
  3. second
  4. third

でした。

Test.jar にエクスポートして起動(ダブルクリックやコマンドプロンプトから)
Out.txt の内容は

  1. same

でした。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

HouraijiICさんの回答の通りかと思います。

Javaのclass loaderのAPIドキュメントを見ても今一つ詳しく書いてくれてはいないですね。

Class#getResourceはClassLoader#getResourceに移譲されるのですが、その際に相対パスで指定したリソース名は絶対パスに変換されます。指定リソース名を絶対パスに変換する規則では「一つ上の階層を".."で解釈する」といったようなことはしてくれず単純にパッケージ名(の"."を"/"に置き換えたもの)へ指定名を連結して生成されます。よって次のようなものがリソースの絶対パスとして解釈されてしまいます。

"step16/first\first.txt"
"step16/../second/second.txt"

上記はjar内ではなくWindowsのディレクトリー上に直接置かれたクラスに対するClassLoaderではWindowsのファイルパスとして解釈され/\もディレクトリーの区切りとして解釈され..は単にそういう名前のディレクトリーとして解釈されます。ご存知のようにWindowsでもLinuxでも".."は一つ上のディレクトリーを表す単なるディレクトリーなので質問者さんが意図したとおりのファイルを見つけることができます。しかしLinux上で同様のことをすると\はディレクトリーの区切りとはみなしてくれないため同様のトラブルが発生すると思います。

またjarファイルの中にあるクラスのClassLoaderは、指定されたパスをjava.util.jar.JarFile#getJarEntryに渡すパスとして解釈します。jarファイルのエントリー名はディレクトリーに似た階層を表現できはしますがそこには「..を一つ上の階層として解釈する」といったような仕様はなく指定した名前と単純比較されるだけです。このためご質問のような結果となります。

以上よりgetResourceに指定する名前はClassLoaderが解釈できるような名前でなくてはならないことを意識し、どのような場所に置かれたクラスでも同様にアクセスできるようにするためには/を区切りに用い、..は用いないようにするのがよいといえるでしょう。

投稿2017/03/01 02:01

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

FoolHotari

2017/03/01 04:24

今後は ClassLoader で .. を使用しないようにします。 ありがとうございます。
guest

0

ベストアンサー

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/lang/resources.html内
・「位置に依存しない方法でのリソースへのアクセス」
・・「java.lang.Classのメソッドの使用」内の↓辺りではないかと。。。

『上記URLより引用-ここから』
getResourceおよびgetResourceAsStreamメソッドは、指定された名前のリソースを検索します。これらのメソッドは、指定された名前のリソースが見つからないとnullを返します。指定されたクラスに関連するリソースを検索するための規則は、そのクラスのClassLoaderによって実装されます。Classのメソッドは、命名規則の適用後にClassLoaderメソッドに委譲されます。リソース名が「/」で始まる場合は、その名前がそのまま使用されます。そうでない場合は、すべてのピリオド(.)がスラッシュ(/)に変換されてから、パッケージ名が先頭に付加されます。
『上記URLより引用-ここまで』

ということで、ClassLoaderを使った例は検索すれば、多々見つかるので、
とりあえず、提示されたコードをエラー無く
java -classpath Test.jar top.third.step16/Test
するため、下記の様にしてみました。

※first\firstはfirst/firstへ変更。
※t3,t4はクラストップからのパスへ変更。

src |-top | |-third | | |-step16 | | | |-first | | | | |-first.txt | | | |-Test.java | | | |-same.txt | | |-third.txt | |-second.txt
public static void main(String[] args){ Test t1=new Test("same.txt"); //同じ階層のファイル Test t2=new Test("first/first.txt"); //一つ下の階層のファイル Test t3=new Test("/top/second.txt"); //一つ上の階層のファイル Test t4=new Test("/top/third/third.txt"); //一つ上の階層にあるディレクトリにあるファイル }

投稿2017/03/01 00:42

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

FoolHotari

2017/03/01 04:19

main メソッドを訂正したところ、期待通りに読み込まれました。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問