20210428のJavaに関する記事は9件です。

JavaでCognitive ServicesのText Analyticsを使う

私の大学時代の専攻は人工知能でした。そのため、AzureのCognitive Servicesは触ってて楽しいです。普段はHTTPのAPIを素で叩くことが多いですが、Java向けのライブラリも充実しているようなので触ってみました。 Cognitive Servicesには色々な種類がありますが、今回はText Analyticsを試しました。その中でも色んな機能がありますが、今回はテキストの文章から言語を特定するものを使ってみました。 実行結果 先に結果を紹介します。以下のような日本語の文章をCognitive Servicesに食わせると String text = "私の推しメンは乃木坂46の大園桃子さんです。"; 以下の結果になります。 検出した言語は Japanese です。ISO 6391の言語コードは ja 、スコアは 1.000000. 英語を入れてみます。 String text = "Nogizaka46 is a Japanese female idol group produced by Yasushi Akimoto, created as the official rival of the group AKB48. "; ちゃんと判定してくれます。 検出した言語は English です。ISO 6391の言語コードは en 、スコアは 0.990000. Javaプロジェクトの準備 Mavenでプロジェクトを作成して、pom.xmlに以下の依存性を追加します。 <dependency> <groupId>com.azure</groupId> <artifactId>azure-ai-textanalytics</artifactId> <version>5.0.4</version> </dependency> Javaのコードは簡単でこれだけです。xxxとなっている部分については後ほど紹介します。 package com.example; import com.azure.ai.textanalytics.TextAnalyticsClient; import com.azure.ai.textanalytics.TextAnalyticsClientBuilder; import com.azure.ai.textanalytics.models.DetectedLanguage; import com.azure.core.credential.AzureKeyCredential; public class App { public static void main( String[] args ) { TextAnalyticsClient textAnalyticsClient = new TextAnalyticsClientBuilder() .credential(new AzureKeyCredential("xxxxxxxxxx")) .endpoint("https://xxxxxx.cognitiveservices.azure.com/") .buildClient(); String text = "私の推しメンは乃木坂46の大園桃子さんです。"; DetectedLanguage detectedLanguage = textAnalyticsClient.detectLanguage(text); System.out.printf("検出した言語は %s です。ISO 6391の言語コードは %s 、スコアは %f.%n です", detectedLanguage.getName(), detectedLanguage.getIso6391Name(), detectedLanguage.getConfidenceScore()); } } Text Analyticsの設定 コードの準備ができたらAzureのポータルを開いてCognitive Servicesを検索します。以下の画面になったら「cognitive servicesの作成」を押します。 「Text Analytics」は日本語で「テキスト分析」と出てくるので、そのまま選びます。 「作成」ボタンを押します。 リソースグループと名前を入力して価格レベルを選択します。今回はFree F0を選びました。 APIキーをクリックします。 キー1をコピーして、冒頭に紹介したJavaコードに設定します。エンドポイントも同様です。 これだけです。これでJavaのプログラムを実行すれば、最初に紹介した結果になります。 参考ドキュメントはこちらです。 https://github.com/Azure/azure-sdk-for-java/tree/azure-ai-textanalytics_5.1.0-beta.5/sdk/textanalytics/azure-ai-textanalytics
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メソッドからオブジェクト指向へ

■ メソッドを分けることについて  なぜ必要なのか ・可読性(読みやすさ) ・柔軟性(修正のしやすさ) ・再利用性(使いまわしやすさ) mainメソッド内にすべてのソースコードを記述して把握しきれるような業務システムはまず存在しないだろう 例えばATMのシステムをイメージとして考えたときに ・受付(50行) ・残高問合せ(50行) ・入出金(50行) ・振込(50行) という4つの仕事をするものとして、これらすべてのソースコードを、実行するmainメソッド内に記述し、修正が必要となった場合に、まずこれら200行のコードの中から修正箇所を探さなければならない 通常の数千、数万行にもなる業務システムでは尚更大変な作業となる 受付メソッドや残高問合せメソッドのように部品化しておくことで、修正範囲もある程度特定ができ、プログラム全体も分割した部品の組み合わせであるため見やすくなる またコードを書いていて同じ作業をさせたい部分が出てくれば、同じメソッドを再利用して余計なコードを記述せず、手間も省くことができる、ということ 以下、渡した2つの引数を足して戻すだけの計算メソッド public static void main(String[] args) { int sum1 = calc(10, 20); System.out.println(sum1); int sum2 = calc(20, 40); System.out.println(sum2); public static int calc(int a, int b) { int ans = a + b; return ans; } } ■ クラス、パッケージに分ける メソッド分けしても増えすぎれば読みづらいことには変わらないため、クラスやパッケージとしてまとめることで解消する 以下、mainメソッドのクラス、渡された引数で足し引きのみ行うクラス、掛ける割るのみ行うクラスに分類 【mainメソッドのクラス】 public class Calc { public static void main(String[] args) { int plus = Plussub.tasu(10, 20); System.out.println(plus); double division = Muldiv.waru(10, 20); System.out.println(division); } } 【加算減算クラス】 public class Plussub { public static int tasu(int a, int b) { int plus = a + b; return plus; } public static int hiku(int a, int b) { int subtraction = a - b; return subtraction; } } 【乗算除算クラス】 public class Muldiv { public static int kakeru(int a, int b) { int multiplication = a * b; return multiplication; } public static double waru(double a, double b) { double division = (a / b); return division; } } メソッドをクラス分けするとmainメソッドのクラスからはスコープの外になるため、クラス名.メソッド名で指定して呼び出す必要がある また同じ要領でパッケージ分けした際にはパッケージ名.クラス名.メソッド名(完全修飾名)で呼び出すかインポートする必要がある ■ オブジェクト指向に沿った部品化 これまでは、あくまでmainメソッド内の作業を他のメソッドやクラスとして分割していただけだが、オブジェクト指向はより可読性、柔軟性、再利用性を向上させるために、ヒトやモノなど現実世界のものになぞらえてクラスに分割し、プログラムを作成するという考え方 各オブジェクトはそれぞれプログラムに対して負うべき責任を持ち、それに対してフィールド(属性)とメソッド(振る舞い)が定義される 部品化というが、正確には部品(オブジェクト)そのものではなく、クラスという設計図を定義する mainメソッドを持つクラスはその設計図を生み出し(インスタンス化し)動かす。 以下、自己紹介をする人の設計図であるHumanクラスをmainメソッドを持つクラスでインスタンス化させ、TanakaとYamadaという2つ(人)のオブジェクトを生成させる mainメソッドを持つクラス public class Main { public static void main(String[] args) { Human h1 = new Human(); h1.name = "Tanaka"; h1.age = 25; h1.address = "Tokyo"; h1.hp = 100; h1.greet(); Human h2 = new Human(); h2.name = "Yamada"; h2.age = 28; h2.address = "Kanagawa"; h2.hp = 130; h2.greet(); { { 【自己紹介するHumanクラス】 public class Human { String name; int age; String address; int hp; public void greet() { hp = hp - 10; System.out.print("My name is " + name + "."); System.out.print("I'm " + age + " years old."); System.out.print("I live in " + address + ". ");    System.out.println("Current HP is " + hp + "."); } } 指定した情報を受け取って自己紹介を行うという「責務」を持つ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure SDK for Java におけるリトライについて

Azure SDK for Java におけるリトライについて 前回につづいてリトライについてです。 オンプレ環境でアプリケーションを実装している時は、リトライなど気していなかった人も多かったのではないかと思いますが、Azure などのクラウド環境上では、メンテナンスによってサービスが再起動したり、ネットワークの中断なども顕著に発生するため、リトライの実装は必須になっております。 Azure のドキュメントに、「Azure サービスの再試行ガイダンス」というものがありますので、まずはこれを読みましょう、必読です。ここには、各コンポーネントと SDK がどういうリトライを行っているか説明されています。 Azure サービスの再試行ガイダンス - Best practices for cloud applications | Microsoft Docs Cosmos DB 先ほどのドキュメントにある表をみると、「サービスでネイティブ」 かつ、 ポリシーは「構成不可」を書かれていて、いまいちピンときません。他が「クライアントでネイティブ」と書かれているところから類推すればクライアントSDKではなく、Cosmos DBがサービスレベルでなんかしてくれるようにも読めます。 また、Cosmos DBの再試行メカニズムの章をよむと、スロットリングに関連したリトライの設定はできそうです。文書ではC#の例ですが、Javaでは、ThrottlingRetryOptions で指定できます。 では、他の場合はどのようなリトライをしているのでしょうか。気になるので SDKのソースを探索してみました。 ClientRetryPolicy#shouldRetry が定義されていて、そこに色々なケースでリトライの可否を判定しています。 リトライすべきかどうかは、SDKが適切に判定してくれるということで、実装はちゃんとされているので安心して利用できます。心配性な人は、SDKを読み込んで検証してみてください。リトライは、なかなか再現できないのがつらいところですけど。 また、Javaの話でいえば、いかにもリトライの記述があって、 Azure Cosmos DB Java SDK v4 の診断およびトラブルシューティング | Microsoft Docs 引用すると、 読み取りおよびクエリ IO エラーは、エンド ユーザーにはわからないまま SDK によって再試行されます。 書き込み (Create、Upsert、Replace、Delete) はべき等では "なく"、したがって SDK は必ずしも無条件に、失敗した書き込み操作を再試行できるわけではありません。 エラーを処理して再試行するには、ユーザーのアプリケーション ロジックが必要です とのことなので、利用する際は注意してください。 Storage Blob 次はストレージです。表では、「クライアントでネイティブ」、ポリシーの構成には「プログラムによる」と書かれています。 ストレージには、その名の通り RequestRetryOptions クラスが用意されていり、クライアントインスタンスをビルドするときに設定できます。デフォルト時の動作は、ハードコーディングされていて、指数関数タイプの4回くらいの感じになっています。デフォルト設定でも問題ないと思います。 自力記述するときは以下のような感じになります。 RequestRetryOptions retry = new RequestRetryOptions( RetryPolicyType.EXPONENTIAL, 3, // max tries 3, // time out in seconds 5000L, // retry delay milli seconds 60000L, // max retry delay milli seconds null); BlobServiceAsyncClient storageClient = new BlobServiceClientBuilder() .retryOptions(retry) .buildAsyncClient(); SDKでは以下のあたりで、リトライしているっぽいです。軽くソースを見た限り、リトライ回数はみているけど、ディレイとかは独自に計算していて、Optionの意味がよく分らない感じでした。 まとめ ということで、いくつかのSDKについて、リトライについてまとめてみました。ドキュメントは豊富にありますが、言語毎にとっちらかっている印象は拭えません。地道にググり、最終的にはSDKのソースにあたりましょう。また、マイクロソフトのドキュメントでは、リトライの訳語が「再試行」で統一されているようですので、「再試行」でググった方がいいかもしれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java XMLを Excelに変換

XMLは、ファイルにデータを書き込むときの記述形式の1つで、種類の異なるソフトウェア間でデータを交換するための形式として利用されます。今回はSpire.XLS for Javaというライブラリを利用して、XMLを Excelに変換する方法を紹介します。  下準備 1.E-iceblueの公式サイトからFree Spire. XLS for Java無料版をダウンロードしてください。 2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire. XLS.jarを参照に追加してください。 import com.spire.xls.*; public class xmltoexcel { public static void main(String[] args) { //xmlファイルをロードします。 Workbook wb = new Workbook(); wb.loadFromXml("test.xml"); //xlsxで保存します。 wb.saveToFile("xmltoExcel.xlsx",FileFormat.Version2013); //xlsで保存します。 wb.saveToFile("xmltoExcel.xls",FileFormat.Version97to2003); } }            
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

正規表現 攻略法

理由・背景 Javaのしっかりした本を読もうとしたら正規表現が出てきて、非常に難儀した。 終了条件 改訂版をつけた時 ブレークダウン なぜ5回 なんでできないのか?→ 正規表現そのものわからない。 正規表現わかればできるのか → Javaとしてわかってないから なぜ → 手を動かしてないから なぜ → 適当な教材がないから なぜ → 英語のチュートリアルでやってないから。 ロードマップ 正規表現をマスターする。 ドットインストールやる https://dotinstall.com/lessons/basic_regexp_v2 英語のチュートリアルやる。 https://www.youtube.com/watch?v=sa-TUpSx1JA Udemyやる。 https://www.udemy.com/course/cfmenzkv/learn/lecture/21899748?start=0#content サルにもわかる正規表現入門やる。 https://userweb.mnet.ne.jp/nakama/ Javaのなかで正規表現を使う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java ExcelをPDFに変換

今日はSpire.XLS for Javaという無料のライブラリを使ってExcelをPDFに変換する方法を紹介していきます。さあ、行きましょう!   下準備 1.E-iceblueの公式サイトからFree Spire. XLS for Java無料版をダウンロードしてください。 2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire. XLS.jarを参照に追加してください。   import com.spire.xls.*; public class ExcelToPDF { public static void main(String[] args) { //Excelファイルをロードします。 Workbook wb = new Workbook(); wb.loadFromFile("test.xlsx"); //PDFで保存します。 wb.saveToFile("ToPDF.pdf",FileFormat.PDF); } } 実行結果    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Intellij import setting

Optimize imports on the fly Layout static import separately
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android/Java】権限の取得方法から取得後の処理

今回は、 アプリ起動時にカメラ、ストレージ、位置情報権限を取得したい 途中で位置情報権限を許可しないに変えることを想定 ボタン押下時に位置情報権限がない場合は権限取得OSダイアログ表示 をするための方法をまとめる。 複数の権限を取得する方法 requestPermissionsを扱うが、パッと調べた限りだと3つある。 1.ActivityCompat#requestPermissions public static void requestPermissions (Activity activity, String[] permissions, int requestCode) そもそもActivityCompatクラスって何なの?と気にしたことがなかったのでそのついで公式ドキュメントをみると Helper for accessing features in Activity. とあった。このクラスのメソッドの大半は引数にActivityが必要だったので、必要になったら確認することにする。 2.Activity#requestPermissions public final void requestPermissions (String[] permissions, int requestCode) ActivityクラスはAPI1からなので、Androidの元祖みたいなものだと思うが、このrequestPermissionsは意外にもAPI23から。 3.Fragment#requestPermissions public final void requestPermissions (String[] permissions, int requestCode) ただこれはAPI23で追加されたもののAPI28で非推奨となっているので注意。この代わりに何を使えば良いかは後述。 以上の3つは使える環境に合わせてどれを使っても変わらない気もする(一語一句公式の説明を読んだわけではないが)。今回は実際に使っていた環境がFragmentクラスだったので(今や非推奨だが)、3のFragment#requestPermissionsを扱う 取得したい権限はManifestに記載する必要がある。AndroidManifest.xmlでapplicationタグの上に <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" /> ACCESS_MEDIA_LOCATIONはAPI29からの権限になるが、OSバージョン分岐せずにしていても特に大丈夫そう。この権限は Allows an application to access any geographic locations persisted in the user's shared collection. とあるが、確か画像から位置情報を取得するときにAPI29以上なら必要だった覚えが。。(残課題) Fragmentクラス内で private static final int REQUEST_PERMISSION = 1000; private static String[] permissions = { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_MEDIA_LOCATION, Manifest.permission.CAMERA }; /* * 中略 */ ActivityCompat.requestPermissions(this.getActivity(), permissions, REQUEST_PERMISSION); これでpermissions配列内の権限全てがチェックされ、不足している権限OSダイアログが表示される。 権限を許可・拒否した次の処理 一度でも実装したことがある人には周知のことだが、以下のように書くと権限取得結果関係なしに次の処理が実行されてしまう ActivityCompat.requestPermissions(this.getActivity(), permissions, REQUEST_PERMISSION); //次の処理 doNextTask(); もし権限を取得できたら次の処理を実行したい場合はonRequestPermissionsResultをオーバーライドする必要がある。 @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { if (requestCode == REQUEST_PERMISSION_LOCATION) { if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //許可された場合 doNextTask(); } } } 第一引数(requestCode)はrequestPermissionsの第三引数になるので、上のサンプルだとREQUEST_PERMISSION_LOCATIONと一致していたらOK。 第三引数(grantResults)の0番目はユーザーがOSダイアログで許可したのかどうかの結果が返ってくる。  許可:PERMISSION_GRANTED  拒否:PERMISSION_GRANTED  要素数0:ユーザーがキャンセルした onRequestPermissionsResultが呼ばれないことがあった 実際に実装していると、なぜかFragment内でrequestPermissionsを実行してもonRequestPermissionsResultが呼ばれずに時間を要したことがあった。ちなみにとある修正をする前までは普通に動いていたのだが、なぜか関係ないと思っている修正をした後だとNGになった。 呼ばれない原因・対策を調べ、こちらの記事を読んでみたものの該当する内容はなし。 結果としてはFragmentクラスでもonRequestPermissionsResultをオーバーライドしていたが、親ActivityクラスでもonRequestPermissionsResultをオーバーライドしていていたことが原因だった。 Fragment#requestPermissionsでも親Activity側のonRequestPermissionsResultが呼ばれる様子。なのでその時は全て親Activity側にまとめるように修正した。 クリックイベント時に権限あるかないかみて処理を分ける ボタン押下時に位置情報権限がある場合は任意の処理を、権限がない場合は権限取得OS表示するように実装 //細かい設定は省略 Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { //位置情報権限が既にある場合 } else { //ない場合は再度リエクエスト requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERMISSION_LOCATION); } } }); これで位置情報権限は取得されるまで要求がループされます。 参考文献(本文中で記載していないもの)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】権限の取得方法から取得後の処理

今回は、 アプリ起動時にカメラ、ストレージ、位置情報権限を取得したい 途中で位置情報権限を許可しないに変えることを想定 ボタン押下時に位置情報権限がない場合は権限取得OSダイアログ表示 をするための方法をまとめる。 複数の権限を取得する方法 requestPermissionsを扱うが、パッと調べた限りだと3つある。 1.ActivityCompat#requestPermissions public static void requestPermissions (Activity activity, String[] permissions, int requestCode) そもそもActivityCompatクラスって何なの?と気にしたことがなかったのでそのついで公式ドキュメントをみると Helper for accessing features in Activity. とあった。このクラスのメソッドの大半は引数にActivityが必要だったので、必要になったら確認することにする。 2.Activity#requestPermissions public final void requestPermissions (String[] permissions, int requestCode) ActivityクラスはAPI1からなので、Androidの元祖みたいなものだと思うが、このrequestPermissionsは意外にもAPI23から。 3.Fragment#requestPermissions public final void requestPermissions (String[] permissions, int requestCode) ただこれはAPI23で追加されたもののAPI28で非推奨となっているので注意。この代わりに何を使えば良いかは後述。 以上の3つは使える環境に合わせてどれを使っても変わらない気もする(一語一句公式の説明を読んだわけではないが)。今回は実際に使っていた環境がFragmentクラスだったので(今や非推奨だが)、3のFragment#requestPermissionsを扱う 取得したい権限はManifestに記載する必要がある。AndroidManifest.xmlでapplicationタグの上に <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" /> ACCESS_MEDIA_LOCATIONはAPI29からの権限になるが、OSバージョン分岐せずにしていても特に大丈夫そう。この権限は Allows an application to access any geographic locations persisted in the user's shared collection. とあるが、確か画像から位置情報を取得するときにAPI29以上なら必要だった覚えが。。(残課題) Fragmentクラス内で private static final int REQUEST_PERMISSION = 1000; private static String[] permissions = { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_MEDIA_LOCATION, Manifest.permission.CAMERA }; /* * 中略 */ ActivityCompat.requestPermissions(this.getActivity(), permissions, REQUEST_PERMISSION); これでpermissions配列内の権限全てがチェックされ、不足している権限OSダイアログが表示される。 権限を許可・拒否した次の処理 一度でも実装したことがある人には周知のことだが、以下のように書くと権限取得結果関係なしに次の処理が実行されてしまう ActivityCompat.requestPermissions(this.getActivity(), permissions, REQUEST_PERMISSION); //次の処理 doNextTask(); もし権限を取得できたら次の処理を実行したい場合はonRequestPermissionsResultをオーバーライドする必要がある。 @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { if (requestCode == REQUEST_PERMISSION_LOCATION) { if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //許可された場合 doNextTask(); } } } 第一引数(requestCode)はrequestPermissionsの第三引数になるので、上のサンプルだとREQUEST_PERMISSION_LOCATIONと一致していたらOK。 第三引数(grantResults)の0番目はユーザーがOSダイアログで許可したのかどうかの結果が返ってくる。  許可:PERMISSION_GRANTED  拒否:PERMISSION_GRANTED  要素数0:ユーザーがキャンセルした onRequestPermissionsResultが呼ばれないことがあった 実際に実装していると、なぜかFragment内でrequestPermissionsを実行してもonRequestPermissionsResultが呼ばれずに時間を要したことがあった。ちなみにとある修正をする前までは普通に動いていたのだが、なぜか関係ないと思っている修正をした後だとNGになった。 呼ばれない原因・対策を調べ、こちらの記事を読んでみたものの該当する内容はなし。 結果としてはFragmentクラスでもonRequestPermissionsResultをオーバーライドしていたが、親ActivityクラスでもonRequestPermissionsResultをオーバーライドしていていたことが原因だった。 Fragment#requestPermissionsでも親Activity側のonRequestPermissionsResultが呼ばれる様子。なのでその時は全て親Activity側にまとめるように修正した。 クリックイベント時に権限あるかないかみて処理を分ける ボタン押下時に位置情報権限がある場合は任意の処理を、権限がない場合は権限取得OS表示するように実装 //細かい設定は省略 Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { //位置情報権限が既にある場合 } else { //ない場合は再度リエクエスト requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERMISSION_LOCATION); } } }); これで位置情報権限は取得されるまで要求がループされます。 参考文献(本文中で記載していないもの)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む