🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Java

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

Q&A

解決済

2回答

1336閲覧

Java とあるフォルダの中のHTMLファイルのみを取得し、更にそのHTMLファイルの中の要素を取得しcsvファイルに書き残したい。

Pro01x19

総合スコア17

Java

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

0グッド

0クリップ

投稿2021/02/22 05:23

編集2021/02/23 22:12

前提・実現したいこと

javaの基礎学習をつい先日1周した者です。
実践的な何かを作りたく、タイトルのようなものを想定し現在作成中です。

作り方が分からなくなってしまい、長時間手が止まってしまったのでここに記述し、
他者にみてもらう事で整理をしようと思っております。

かなり迷走していて基礎も調べながらの記述になっているため
コードが読みづらいかもしれませんがご容赦ください。

・csvファイルはindex年月日時分秒のフォーマットを使い作成
・読み込みたいフォルダはエディターのデバック環境に引数として登録済み
・csvファイルには HTMLファイル名とタイトルを1行ずつ記述する

上記のようなものを目標に作成しております。

該当のソースコード

csvファイルの作成 htmlファイル名取得 htmlファイル名をcsvファイルに書き込み までの完成したコード import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.io.BufferedWriter; import java.io.FileWriter; public class App { public static void main(String[] args) throws Exception { // void型でmainを定義 Calendar c = Calendar.getInstance(); //カレンダークラスにより現在日時を取得 DateFormat myFormat = new SimpleDateFormat("_yyyy_MM_dd_HH_mm_ss"); // 日時のフォーマットを設定 String FileName = "index" + myFormat.format(c.getTime()); // FileNameにファイルネームを定義 File newFile = new File("/Users/mono/tmp/" + FileName + ".csv"); // Fileを/Users/mono/tmp/FileName.csvで作成 try{ if(newFile.createNewFile()){ // もしファイル作成できたら System.out.println(FileName + "のファイルの作成に成功"); // 上記の文章をコマンドラインに表示 }else{ // ファイル作成出来ていなかったら System.out.println("ファイルの作成に失敗"); // 上記の文章をコマンドラインに表示 } }catch(IOException e){ System.out.println(e); } if (args.length != 0){ // 引数の数が0+1個だったら File dir = new File(args[0]); // 引数に設定してあるフォルダをdirに定義 File[] fileList = dir.listFiles(); // fileListにフォルダ内のファイルを配列として格納 for(int i = 0; i < fileList.length; i++){ // fileListの数だけ処理を繰り返す if(fileList[i].getName().contains(".html")){ // .html拡張子のファイル名を取得 if(checkBeforewritefile(newFile)){ BufferedWriter bw = new BufferedWriter(new FileWriter(newFile)); // newFileにまとめて書き込む準備をバッファでする。 bw.write(fileList[i].getName()); // 処理中のfileListの名前をnewFileに書き込む bw.close(); // 1行の処理のため一度閉じる System.out.println(fileList[i].getName() + "のファイル名を書き込みました"); // 書き込めた場合この文章をコマンドラインに表示 } else{ System.out.println("書き込めませんでした"); } } } } } private static boolean checkBeforewritefile(File newFile) { if(newFile.exists()){ if(newFile.isFile() && newFile.canWrite()){ return true; } } return false; } }
作りたいものの作りかけのコード import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.io.Closeable; public class App { public static void main(String[] args) throws Exception { //カレンダークラスにより現在日時を取得 Calendar c = Calendar.getInstance(); // 日時のフォーマットを設定 DateFormat myFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); // ファイル名を定義 String FileName = "index" + myFormat.format(c.getTime()); // File newFile = new File("/Users/mono/tmp/" + FileName + ".csv"); try{ if(newFile.createNewFile()){ System.out.println(FileName + "のファイルの作成に成功"); }else{ System.out.println("ファイルの作成に失敗"); } }catch(IOException e){ System.out.println(e); } if (args.length != 0){ //引数必ず1つであること //エディターに引数設定済み File dir = new File(args[0]); //設定からフォルダを引数として受け取る File[] fileList = dir.listFiles(); //フォルダの中身を配列として格納 if(fileList != null){ //もしフォルダの中身が終わってないなら for(int i = 0; i < fileList.length; i++){ //繰り返し処理をします try{ if (fileList[i].getName().contains(".html")){//もし html を含むファイル名があるならば if (checkBeforewritefile(newFile)){ // もしcsvファイルに書き込みをするならば BufferedWriter bw = new BufferedWriter(new FileWriter(newFile)); // csvファイルにファイル書き込みをする宣言 // BufferedReader br = new BufferedReader(new FileReader(fileList[i])); // ファイルを読み込む準備 // String line; // String型を定義 // while((line = br.readLine()) != null); // 行がなくなるまで1行ずつ処理していく // String regex =" <(title)>.*?<\>"; // これを取得したいと定義 // Pattern p = Pattern.compile(regex); // 定義した物をパターンと定義 // check(p,line); // 1行ずつパターンがないかチェック System.out.println(fileList[i].getName() + "のファイル名を書き込みました"); bw.write(fileList[i].getName()); //フォルダから取得したhtmlを含むファイル名をcsvファイルに書き込み bw.close(); } }else{ System.out.println("書き込めませんでした"); } }catch(IOException e){ System.out.println(e); }finally{ } } } } } private static boolean checkBeforewritefile(File newFile) { if(newFile.exists()){ if(newFile.isFile() && newFile.canWrite()){ return true; } } return false; } private static void check(Pattern p, String line) { Matcher m = p.matcher(line); if (m.find()){ // バッファ 取得したタイトル で書き込み }else{ // バッファ タイトルなし で書き込み } } }

分かっていない事

・タイトルにマッチしている物を書き込む方法
・繰り返しの処理を何段階かに分けて書く方法

質問内容やコードが見づらく申し訳ございません。
ご指摘があった所を一つずつなおしていきますので何卒よろしくお願いします。

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

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

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

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

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

dodox86

2021/02/22 05:34

> HTMLファイルの中の要素を取得 というところで結構面倒(HTMLのパース。初心の方には難しい)です。その部分は後まわしでまずはファイル名のみ、などとしてみてはいかがでしょうか。ひとつひとつ理解、実装するのが大事です。
Pro01x19

2021/02/22 05:36

ありがとうございます! ご指摘の通りだと思います。そのように部分的に進めて参りまして 1つにまとめようとした段階で混乱してしまってます...
dodox86

2021/02/22 05:51

ご提示のコードで全部ですか?せめてコンパイルが通る内容とした方が良いと思います。(importなど省いてますよね)
m.ts10806

2021/02/22 05:54

作りながら考えるから難しいのでは、と思います。
Pro01x19

2021/02/22 05:57

部分的に行ったものは ・csvファイルの作成 ・フォルダ内のhtmlファイル名の取得 ・指定のhtmlファイルの全文の取得 です。 繰り返しの処理と正規表現を用いれば出来ると思っていたので全部まとめてみた所、ご指摘の通りコンパイルが通らなくなってしまいました。 こちらのソースコードにimportを省いてしまっていたのは完全に私のミスです...すぐ修正いたします。
m.ts10806

2021/02/22 05:58

下手に自作するよりライブラリ使ったほうが良いでしょうね。正規表現もそうですし、「htmlとして正しく解釈できるか」とか多角的な視点とスキルが必要です。
Pro01x19

2021/02/22 06:03

m.ts10806さん ご指摘ありがとうございます。基礎の知識を活かせるか。という自己学習の一貫だったため今回ライブラリは使用しないでやってみたいと思っています。フォルダ内のhtmlファイルのみを取得する事自体は部分的な実装で出来ていました。が仰るとおり正規表現には自信がないのが現状です。 正規表現以外の部分を形にするのは難しいでしょうか?
m.ts10806

2021/02/22 06:06

私が要件把握しきれてないだけかもしれませんが、「正規表現以外の部分」とは具体的にどこになりますか?
Pro01x19

2021/02/22 06:08

・csvファイルの作成 ・フォルダ内のhtmlファイルのみを取得 ・htmlファイル名をcsvファイルに記述 だけの物ですね。
m.ts10806

2021/02/22 06:14

2021/02/22 14:57のコメントからすると下記はやってみている、しかしコンパイル通らない、ということですね ・csvファイルの作成 ・フォルダ内のhtmlファイルのみを取得
Pro01x19

2021/02/22 06:17

その機能単体では通っている形ですね... 失礼しました。先ほどの3点のみのコードは現時点で作成出来てしまいました。。 そこにタイトルのみの取得、書き込みのコードがあるからコンパイルできないみたいでした。
dodox86

2021/02/22 06:25

ちょっと迷走しているようですが、結局のところ今、問題にしているのはどこなのでしょう。「タイトルのみの取得、書き込みのコードがコンパイルできない」ですか? (そこを直したらまた別の問題が出る可能性はありますが)
Pro01x19

2021/02/22 06:27

dodox86 さん そうです!迷走しておりましたがその部分だけが難しく、また他のコードと繋げるとエラーになってしまう形ですね
dodox86

2021/02/22 06:59

> その部分だけが難しく、また他のコードと繋げるとエラーになってしまう形ですね 別の部分について既に回答をいただいていますが、おおもとの問題としては色んな機能をひとつにしようとして混乱、迷走していることにあると 思います。HTMLファイルからタイトルを取り出すことに関して、「難しい」ということと「他のコードに繋げるとエラー」と言うのは別の問題です。 「難しい」部分に関しては<HEAD>~</HEAD>中の<TITLE>要素を取り出す、「他のコードに繋げるとエラー」の部分に関しては動いていたコードをそのままコピペするのではなく、適切にメソッドに切り出すことです。static void main()メソッドの中に詰め込むとややこしいことになると思います。
Pro01x19

2021/02/23 22:15

dodox86さん ありがとうございます。今まで出来ていた機能までのコードを追記いたしました。そのコードとは別に要素抽出機能を追加していく形にしようと思います。
dodox86

2021/02/24 00:12

私の回答はHTMLファイルから<TITLE>タグの要素を取り出す一例であり、言いたいことはどちらかというと「機能ごとにメソッドなどの処理単位を分けましょう。その方が作りやすいしテストもデバッグもやりやすいですよ」と言うことなので、回答のコードにこだわる必要はないです。既にいただいた回答を含め、それをヒントに極力自力で解決されることを希望します。正規表現が分からなければ別の質問にするか、正規表現を使わない別の方法をがんばって作るべきです。一応申し添えておきますと、質問者であるPro01x19さんの要望するプログラムを完成させるためにコードややり方のひとつひとつにアドバイスをしていくことは無理であり、サイトの主旨にも沿っていないと考えているので、そういうことをお望みであれば私では恐らくお役に立てません。
guest

回答2

0

ベストアンサー

HTMLのタイトル部分は簡単に考えると<TITLE>タグに収められているわけです。HTMLの構造を意識し、全体を厳密にチェックするなら既存のHTMLパーサーの類を使うべきかと思いますが、自分の勉強の為もあるなら自力でタグの関係性、妥当性を考慮して取り出すようにコードを書いてもよいでしょうし、正規表現の勉強もかねて簡単に(≒少々雑に)取り出すことも可、でしょう。

以下は簡単に<HEAD>...<TITLE>...</TITLE>...</HEAD>に収められたHTMLのタイトルを正規表現で取得するサンプルコーです。HTMLのタグは大文字小文字を区別しないし、複数行にまたがることも考えられるので、その辺は簡単に考慮しています。

Java

1import java.io.BufferedReader; 2import java.io.FileReader; 3import java.io.IOException; 4import java.util.regex.Pattern; 5import java.util.regex.Matcher; 6 7public class Main { 8 public static void main(String[] args) { 9 Main obj = new Main(); 10 try { 11 obj.run(args[0]); 12 } catch (Exception ex) { 13 ex.printStackTrace(); 14 } 15 } 16 17 private void run(String fileName) { 18 try { 19 String result = getHtmlTitle(fileName); 20 System.out.println("result: " + result); 21 // で、result をファイルに書き込めば良い。 22 } catch (Exception ex) { 23 ex.printStackTrace(); 24 } 25 } 26 27 public String getHtmlTitle(String fileName) 28 throws IOException 29 { 30 String result = ""; 31 32 BufferedReader br = null; 33 try { 34 // <TITLE>.*</TITLE>部分を抽出。大文字小文字は区別しない。 35 String regex ="<head>.*<title>(.*)</title>.*</head>"; 36 Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); 37 38 String content = ""; 39 String line; 40 br = new BufferedReader(new FileReader(fileName)); 41 while((line = br.readLine()) != null) { 42 // 複数行に分かれている場合を考慮し、連結する。 43 content += line; 44 Matcher matcher = pattern.matcher(content); 45 if (matcher.find()) { 46 System.out.println("match: " + matcher.group(0)); 47 result = matcher.group(1); 48 // 発見したらそれ以上検索する必要は無いので、break 49 break; 50 } 51 } 52 } finally { 53 if (br != null) { 54 br.close(); 55 } 56 } 57 58 return result; 59 } 60}

以下のような内容のHTMLファイルがあるとして、

HTML

1<!DOCTYPE html> 2<html> 3 <head> 4 <TITLE>HTML Title 5 </title> 6 </head> 7 <body> 8 Body content. 9 </body> 10</html>

このサンプルコードを実行するとタイトル部分を抽出し、出力します。

CMD

1C>java Main.java sample1.html 2match: <head> <TITLE>HTML Title </title> </head> 3result: HTML Title

その他:

複数の機能をひとつのプログラムに組み込むときに考えが迷走してしまうのは、ひとつの原因として機能の切り分けが不完全であるために、思考があちこちに飛んでしまい、ひとところに集中できないというのがあります。例えば以下のような処理であれば、ループも処理のネストも深くなりますし、深くはなりつつもコードの前後で変数の関係は生きているので影響範囲を狭めづらく、うまく管理しないとコンパイルエラーも起きやすくなります。

ファイル数分ループ 対象のファイルの行数分ループ 1行ごとの処理色々 if 対象の行、データか 対象のデータを別ファイルに追加

そんなものは論理的にひとつの機能、メソッドに切り出します。例えば「ファイル名を引数としたファイル1つだけを処理するメソッド」にすれば、見た目のループのネストも少なくなり得るし、テストやデバッグもし易いはずです。実例として先に挙げたサンプルコードは、「HTMLファイル名を引数として、そのファイルから"タイトルだけ"を抽出する」メソッドです。

メインで実行するクラスにつきもののstatic void main(String[] args) メソッドですが、このメソッド内でコードを書き進めてしまうと、staticであるが故にインスタンスフィールドも使えず、他のメソッドへも切り出しづらいです。staticメソッドを書き続けなければなりません。そんな場合はstatic void main(String[] args)は単なる実行時のエントリポイントである、ということを踏まえ、mainメソッドではインスタンスを生成し、そのインスタンスのメソッドを実行するようにすれば適切かつ使い良いようにクラスを扱えます。これも先のサンプルコードを見てみてください。Main obj = new Main(); .. obj.run(); で実行するようにしています。

投稿2021/02/23 03:20

dodox86

総合スコア9256

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

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

Pro01x19

2021/02/25 03:47

ありがとうございました! Javaの学習に置いてそのような視点が重要かを何も分かっていない私に何度も丁寧に教えていただきありがとうございました。 回答いただいたコードにはどのような処理がされているか、今の私には分からない点もありますが、自身が説明可能なコードのみで出来ないかを試しながら書き方を参考にしてきます!
dodox86

2021/02/25 03:52

> 自身が説明可能なコードのみで出来ないかを試しながら書き方を参考にしてきます! それはとても良いことだと思います。まだ理解できないものをコピペして使うより、今、手持ちの技術で自分がやりたいことを実現するよう努力するのも、良い勉強になるはずです。がんばってください。
guest

0

htmlのparseは他の方が頑張ってくれているようなので、ループについて。
引数の数が足りない、htmlファイルが無いの様に処理の継続が不可能な場合は
早期returnで処理を抜けるとシンプルにコードが書けます。

思考としては「この条件にマッチしたら処理を続ける」ではなく
「条件にマッチしなかったら処理を終了」と考えると良いでしょう。
「この条件にマッチしたら処理を続ける」で処理を続けると
思考もネストしていき記述するコードも思考に伴ってネストしていきます。

Java

1import java.io.File; 2import java.io.IOException; 3import java.nio.charset.StandardCharsets; 4import java.nio.file.Files; 5import java.nio.file.Path; 6import java.text.DateFormat; 7import java.text.SimpleDateFormat; 8import java.util.ArrayList; 9import java.util.Calendar; 10import java.util.Collections; 11import java.util.List; 12import java.util.Objects; 13 14public class App { 15 16 public static void main(String[] args) throws Exception { 17 18 //カレンダークラスにより現在日時を取得 19 Calendar c = Calendar.getInstance(); 20 21 // 日時のフォーマットを設定 22 DateFormat myFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); 23 24 String FileName = "index" + myFormat.format(c.getTime()); 25 26 Path path = Path.of("/Users/mono/tmp/", FileName, ".csv"): 27 var app = new App(); 28 if (!app.validate(args)) { 29 return; 30 } 31 32 List<File> htmlFileList = app.getHtmlFiles(args[0]); 33 if (htmlFileList.isEmpty()) { 34 return; 35 } 36 37 List<String> csvRecords = new ArrayList<>(); 38 for (File file : htmlFileList) { 39 String title = app.getTitle(file); 40 var record = new CsvRecord(file.getName(), title); 41 csvRecords.add(record.toCsv()); 42 } 43 44 try { 45 Files.write(path, csvRecords, StandardCharsets.UTF_8); 46 } catch (IOException e) { 47 System.out.println("ファイルの作成に失敗"); 48 } 49 } 50 51 /** 52 * 引数をチェックする. 53 * 54 * @param args 引数 55 * @return チェック結果 56 */ 57 private boolean validate(String[] args) { 58 if (args.length < 1) { 59 return false; 60 } 61 return true; 62 } 63 64 /** 65 * htmlファイルを取得する. 66 * 67 * @param path Path 68 * @return htmlファイルリスト 69 */ 70 private List<File> getHtmlFiles(String path) { 71 List<File> ret = new ArrayList<>(); 72 File dir = new File(path); 73 File[] fileList = dir.listFiles(); 74 if (Objects.isNull(fileList)) { 75 // 空ならemptyListを返す 76 return Collections.emptyList(); 77 } 78 79 for (int i = 0; i < fileList.length; i++) { 80 fileList[i].getName().endsWith(".html"); 81 ret.add(fileList[i]); 82 } 83 return ret; 84 } 85 86 /** 87 * タイトルを取得する. 88 * 89 * @param file ファイル 90 * @return タイトル 91 */ 92 private String getTitle(File file) { 93 // TODO: htmlファイルからtitleを取得する 94 return ""; 95 } 96 97 static class CsvRecord { 98 99 /** 100 * コンストラクタ. 101 * 102 * @param fileName ファイル名 103 * @param title タイトル 104 */ 105 public CsvRecord(String fileName, String title) { 106 this.fileName = fileName; 107 this.title = title; 108 } 109 110 /** 111 * ファイル名. 112 */ 113 private String fileName; 114 115 /** 116 * タイトル. 117 */ 118 private String title; 119 120 /** 121 * CSV変換. 122 * 123 * @return csv 124 */ 125 public String toCsv() { 126 return String.join(",", this.fileName, this.title); 127 } 128 } 129} 130 131

投稿2021/02/22 06:41

Luice

総合スコア771

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問