業務備忘録

備忘録です

JavaのPathsクラスとURIクラス(Windows)

2023-09-14 01:13:55 | 日記

1.そもそもパスとURIってなんだっけ

パス…ファイルシステムのリソースの在処を示す。 標準のDOSパスの場合、パスは3つの部分から構成される。

  1. ボリュームまたはドライブ文字とそれに続くボリューム区切り記号 (:)。
    =>Cドライブ直下の場合はパスの先頭の"C:"
  2. ディレクトリ名。ディレクトリ区切り文字によって、入れ子になっているディレクトリ階層内でサブディレクトリが分割されます。
    =>ディレクトリ区切り文字はWindowsの場合はバックスラッシュ(\)。
  3. 任意のファイル名。
    =>readme.txtとか。

https://learn.microsoft.com/ja-jp/dotnet/standard/io/file-path-formats

URI

  1. スキーム
    =>http:、ftp:、data:、file:など通信手段を示す
  2. authority =>スキームのあと"//"で始まる部分。ユーザ情報、ホスト、ポートから構成される。 ホストは"www.google.com"、ポートはポート番号。
  3. パス
    =>指定したauthority内のリソースの在処を表す。 上で説明したパスに同じ。
  4. クエリ 指定したauthority内のリソースの在処を表す。 上で説明したパスに同じ。
  5. フラグメント ??

 

2.PathsクラスとURIクラスを利用する

JavaのURIクラスには、上で説明したスキームやクエリを取得するメソッドのほか、URIオブジェクトを生成するcreateメソッドが用意されています。

public static URI create(String str)

パラメータには、URIとして分析可能な文字列を渡します。

2.1. エラーになる表現もある

String pattern = "パスは{0}.\nURIは{1}";
 
URI uri2 = URI.create("file:/pleiades/workspace/JavaStudy/text/sample/test2.txt");
Path path2 = Paths.get(uri2);
System.out.println(MessageFormat.format(pattern,path2.toString() ,uri2.toString()));
 
URI uri3 = URI.create("C:/pleiades/workspace/JavaStudy/text/sample/test2.txt");
Path path3 = Paths.get(uri3);
System.out.println(MessageFormat.format(pattern,path3.toString(), uri3.toString()));
 
URI uri4 = URI.create("./text/test/txt");
Path path4 = Paths.get(uri4);
System.out.println(MessageFormat.format(pattern, uri4.toString(), path4.toString()));
 
URI uri5 = URI.create("file:\\pleiades\\workspace\\JavaStudy\\text\\sample\\test2.txt");
Path path5 = Paths.get(uri5);
System.out.println(MessageFormat.format(pattern, path5.toString(), uri5.toString()));
 

URIクラスのcreateメソッド※を利用して、URIクラスのオブジェクトを作成し、さらに、URIクラスのオブジェクトを利用してPathsクラスのgetメソッドからPathを指定します。
最後に、MessageFormatクラスのformatメソッド※でURIクラスオブジェクトとPathクラスオブジェクトの文字列表現を出力。
適当に4つ並べてみなしたが、この中で正しくPathクラスオブジェクトを取得できるのは1つだけです

※MessageFormat.formatメソッド…第一引数のpatternに変数を埋め込む書式パターン、第二引数以降に埋め込む変数を指定して、メッセージの指定部分のみを変更する。

2.2. 解答編

//パスは\pleiades\workspace\JavaStudy\text\sample\test2.txt.
//URIはfile:///pleiades/workspace/JavaStudy/text/sample/test2.txt
URI uri2 = URI.create("file:/pleiades/workspace/JavaStudy/text/sample/test2.txt");
Path path2 = Paths.get(uri2);
 
//FileSystemNotFoundException:Privider"C"not installed
URI uri3 = URI.create("C:/pleiades/workspace/JavaStudy/text/sample/test2.txt");
Path path3 = Paths.get(uri3);
 
//IllegalArgumentException:missing scheme
URI uri4 = URI.create("./text/test/txt");
Path path4 = Paths.get(uri4);
 
//IllegalArgumentException:Illegal character in opaque part at index 5
URI uri5 = URI.create("file:\\pleiades\\workspace\\JavaStudy\\text\\sample\\test2.txt");
Path path5 = Paths.get(uri5);
 

【失敗例1】

//FileSystemNotFoundException:Privider"C"not installed
URI uri3 = URI.create("C:/pleiades/workspace/JavaStudy/text/sample/test2.txt");
Path path3 = Paths.get(uri3);

ドライブの"C"をスキームとして分析してしまうためエラー。

【失敗例2】

//IllegalArgumentException:missing scheme
URI uri4 = URI.create("./text/test/txt");
Path path4 = Paths.get(uri4);

スキーム(https:など)を指定しない相対パスで生成したURIクラスオブジェクトを引数にしてgetを呼び出すとillegalArgumentExceptionがスローされます。
なお、

Path path4 = Paths.get("./text/test/txt");

のように、URIクラスオブジェクトを利用せず、Pathsクラスのgetメソッドに相対パスを文字列で指定した場合は、正しくリソースのパスを指定することができます。
相対パスで指定した場合、ルートはプロジェクトのフォルダになります(上掲の例だとJavaStudy)。

【失敗例3】

URI uri5 = URI.create("file:\\pleiades\\workspace\\JavaStudy\\text\\sample\\test2.txt");
Path path5 = Paths.get(uri5);

windowsのディレクトリ区切り文字であるバックスラッシュ(/)を利用しているパターン。
"Illegal character in opaque part at index 5"
=>"opaque"はURIのデータ格納部分。バックスラッシュで区切りを表現してはいけない。

【成功例】

//パスは\pleiades\workspace\JavaStudy\text\sample\test2.txt.
//URIはfile:/pleiades/workspace/JavaStudy/text/sample/test2.txt
URI uri2 = URI.create("file:/pleiades/workspace/JavaStudy/text/sample/test2.txt");
Path path2 = Paths.get(uri2);

パスはwindowsの区切り文字であるバックスラッシュで表現されていることがわかります。また、URIのスキーム部はパスとしては表現されないこともわかります。
なお、Ubuntuで実行した場合、

//パスは/pleiades/workspace/JavaStudy/text/sample/test2.txt.
//URIはfile:/pleiades/workspace/JavaStudy/text/sample/test2.txt

パスの区切り文字はスラッシュとなります。

 

 


【Java】StreamAPIの練習をする(mapで別のStreamを生成する)

2023-09-06 22:25:30 | 日記

今回は、貸出処理などにかかわらず、ページ数などの書籍の情報を表示するメソッドを作成します。

1.変えたところ

Proc.java

public void showLendInfoAndBookInfo(Map<String, Book> bookMap) {
    Stream<Book> stream = bookMap.values().stream();
           
        //最も貸し出された書籍を表示する。
        System.out.println("最も貸出された書籍のタイトルは『" +
                stream
                .filter(t->t.getLendCount() > 0)
                .sorted((r1,r2)->r2.getLendCount()-r1.getLendCount())
                .map(s->s.getBookName())
                .findFirst()
                .orElse("#Error!!1冊も貸し出されていません!") + "』です。");
       
        //一番ページ数が少ない書籍を表示する。
        System.out.println("図書館で一番ページが少ない本のページ数は" +
                stream.mapToInt(r->r.getPages())
                .sorted()
                .findFirst()
                .getAsInt() + "です。");
    }

 

2つSysoutしているなかでも、上のほうでは、最も貸し出された書籍を表示します。最初にfilter()で貸し出されたことのある書籍に絞り込んだうえで、貸し出し回数でソートします。
map()はStreamを別のStreamで置き換えるメソッドで、これによってStreamがStream(引数のラムダ式内で書籍名=String型を指定)となります。
findFirst()は、Streamの最初のOptionalを返します。Optionalは値がnullである場合の操作を備えたクラスで、orElseは値がnullである場合は、引数に指定した値を返します。
findFirst()で最も貸し出された書籍を取得し、orElse(値が空でない場合は要素を返し、空である場合は引数に指定した要素を返す)で一冊も書籍が貸し出されていない場合の出力を指定しているというわけです。

下の処理ほうの処理では、Bookオブジェクトからページ数で並べ替えてfindFirst()でページ数が最小の書籍のページ数を取得します。

 

『こころ』を2回、『想像の共同体』を1回貸し出しして、実際にshowLendInfoAndBookInfo()を呼び出しましょう。

最も貸出された書籍のタイトルは『こころ』です。

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed

あれ?

2.そもそもさぁ

StreamはfindFirst()などの終端操作を行うと閉じられてしまい、使いまわしできないようです。一度閉じたStreamを呼び出すとillegalSateExceptionがスローされる。

Stream<Book> stream2 = bookMap.values().stream();
        //一番ページ数が少ない書籍を表示する。
        System.out.println("図書館で一番ページが少ない本のページ数は" +
                stream2
                .mapToInt(r->r.getPages())
                .min()
                .getAsInt()+ "です。");
    }

そもそも「ページ」はint型で管理しているので、わざわざsorted()とかfindFirst()する必要なし。

mapToInt()でIntStream(int型専用のメソッドを備えたクラス)に変換すれば、min()やmax()で最小値/最大値を取得できます。

最も貸出された書籍のタイトルは『こころ』です。

図書館で一番ページが少ない本のページ数は180です。

しかし最大値や最小値、平均などを算出するためにいちいちStreamを作成しなおすのは非常にめんどくさい。

 

IntSummaryStatistics statistics = stream3.map(r->r.getPages()).collect(Collectors.summarizingInt(s->s));

 

System.out.println("図書館で一番長い本のページ数は" + statistics.getMax() + "ページです");

System.out.println("図書館で一番短い本のページ数は" + statistics.getMin() + "ページです");

System.out.println("図書館の本の平均ページ数は" + statistics.getAverage() + "ページです");

System.out.println("図書館の本の冊数は" + statistics.getCount() + "冊です");

IntSummaryStatisticsにcollect(Streamで操作した結果を集約するメソッド)で集約した処理結果を渡すと、最大値や最小値などの情報を保存できます。

図書館で一番ページが少ない本のページ数は180です。

図書館で一番長い本のページ数は530ページです

図書館で一番短い本のページ数は180ページです

図書館の本の平均ページ数は331.25ページです

図書館の本の冊数は8冊です

 

https://gitlab.com/simulacre1/librarytest/-/tree/day1?ref_type=tags