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

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

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

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Java

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

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Q&A

解決済

2回答

1986閲覧

Javaのファイル操作によるグループトータル処理がわかりません

javamurisugiru

総合スコア1

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Java

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

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

0グッド

1クリップ

投稿2021/05/07 18:05

前提・実現したいこと

java初学者です。
DATファイルを用いてファイル操作を行い、グループトータルという処理を行いたいです。
以下、ファイルの例です。

【点数ファイル】
A 01 MATSUMOTO 090 075 100
A 02 KAWAUCHI 100 055 080
A 03 KANBA 040 100 050
A 04 MATSUURA 030 020 010
B 01 ASANUMA 060 090 070
B 02 IBA 050 080 090 
C…以降割愛
(データはHクラスまであり、30件分ほどあります。)

・データは「クラス名、出席番号、名前、英語の点数、数学の点数、国語の点数(すべて半角スペース区切り)」で、クラス名で昇順にソートされています。

・クラス別に英語、数学、国語の平均点を出したいです。

・読み込んだ1レコードを半角スペース区切りごとにspritで分割し、配列に格納した後、配列の0番目の要素であるクラス名を一時的に退避領域に退避させてから、次のレコードのクラス名と比較し、同じ場合には点数を合計し、異なる場合には合計点を人数で割り、平均点を算出し表示しようとしたのですが、アルゴリズムがわからず苦戦しています。

特に退避領域のクラス名と次のレコードのクラス名をどのように比較したらよいのかがわかりません。

どなたかご教授いただければ幸いです。

発生している問題・エラーメッセージ

[出力結果] クラス 英語 数学 国語 _________________________ A 90 75 100 A 20 50 25 B 20 30 23 B 17 25 15 C 4 20 20 C 13 11 3 D 4 9 12 D 4 2 6 D 8 11 8 E 9 1 4 E 8 9 9 F 8 4 8 G 7 7 7 H 3 4 7 H 6 2 0 (一番上のクラス 英語 数学 国語は自分で表示させています) 期待する結果は、 A 62 75 89 B 59 62 75 C 56 67 76 D … (以降割愛) 上記のように1つのクラスにつき一つの結果になるようにしたいです。

該当のソースコード

Java

1MyFileReader mfr = new MyFileReader(scoreFile); 2 3 String line; //1レコードを読み込む 4 String[] data = {}; //読み込んだレコードを格納する配列 5 6 int count = 0; //クラスの人数をカウントする 7 8 /* 主処理ループ */ 9 while((line = mfr.read()) != null) { 10 11 //1レコードデータを半角スペース区切りごとに分割し配列に格納する。 12 data = line.split(" "); 13 14 int eigo = Integer.parseInt(data[3]);//分割したレコードの英語の点数を数値型に変換 15 int suugaku = Integer.parseInt(data[4]);//分割したレコードの数学の点数を数値型に変換 16 int kokugo = Integer.parseInt(data[5]);//分割したレコードの国語の点数を数値型に変換 17 18 String taihi = data[0];//読み込んだレコードのクラス名の退避領域 19 20 int eigoSum = 0; 21 int suugakuSum = 0; 22 int kokugoSum = 0; 23 24 25 /* 集計ループ */ 26 while(line != null || !(taihi.equals(data[0]))) { 27 28 String crass = data[0]; 29 30 if(taihi.equals(crass)) { 31 32 eigoSum = eigoSum + eigo; 33 suugakuSum = suugakuSum + suugaku; 34 kokugoSum = kokugoSum + kokugo; 35 36 count = count + 1; 37 38 39 } 40 41 }//集計ループ 42 43 int eigoAve = eigoSum / count; 44 int suugakuAve = suugakuSum / count; 45 int kokugoAve = kokugoSum / count; 46 47 System.out.print(String.format("%3s ", taihi)); 48 System.out.print(String.format("%4d ", eigoAve)); 49 System.out.print(String.format("%4d ", suugakuAve)); 50 System.out.println(String.format("%4d ", kokugoAve)); 51 52 }//主処理ループ 53 54 mfr.close(); 55 56 } 57 58} 59

試したこと

比較の方法や分岐の書き方などいろいろ試してみましたが思うような結果は得られませんでした。
コードの書き方やそもそもの考え方にかなり問題があるのかもしれないのですが
どこが問題なのかも分からなくなってしまったため、困っています。

補足情報(FW/ツールのバージョンなど)

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

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

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

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

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

guest

回答2

0

ベストアンサー

特に退避領域のクラス名と次のレコードのクラス名をどのように比較したらよいのかがわかりません。

幸いにしてファイル中のデータはクラス名が連続しています。言い方を変えると「1行毎にデータを処理していて、今回処理しようとする行のクラス名が前の行のクラス名と違っていたら、それは新しいクラスの人たちのデータの始まり」ととらえることができます。それ迄のクラスの人たちのデータはその時点で終わりなので、必要に応じて集計処理をしてもよいでしょう。

冷静に考えてみるとそんなに難しいことではなく、自分の目と手を使って計算するのと同様の操作でできるはずです。反対に言うと、まず自分の目と手で計算できないのであれば、プログラムのコードに落とすことはできません。

「クラス名が変わったときを検出する。」これを具体的にコードに落としこむと例えば以下のようになります。

Java

1import java.io.IOException; 2import java.io.BufferedReader; 3import java.io.InputStreamReader; 4 5public class Main { 6 public static void main(String[] args) 7 throws IOException { 8 9 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 10 11 String line; 12 13 int count = 0; // クラスの人数をカウントする 14 String className = ""; 15 16 while ((line = reader.readLine()) != null) { 17 ++count; 18 System.out.print(count + "行目: "); 19 20 //1レコードデータを半角スペース区切りごとに分割し配列に格納する。 21 String[] data = line.split(" "); 22 if (className.equals(data[0])) { 23 System.out.println(data[0] + ": 同じクラスの人が続いている"); 24 } else { 25 System.out.println(data[0] + ": 違うクラスを発見"); 26 className = data[0]; 27 } 28 } 29 System.out.print(count + "人分のデータを処理"); 30 } 31}

以下のような「data.txt」ファイルがあるとして、

PlainText

1A 01 MATSUMOTO 090 075 100 2A 02 KAWAUCHI 100 055 080 3A 03 KANBA 040 100 050 4A 04 MATSUURA 030 020 010 5B 01 ASANUMA 060 090 070 6B 02 IBA 050 080 090  7C 01 KATSUAI 010 020 030

これを当プログラムのデータとして使うと、以下のような実行結果となります。クラスの変わり目を検出できていることが分かると思います。尚、ファイルの読み込みは「標準入力からのリダイレクト」を使っています。

CMD

1C>java Main < data.txt 21行目 A: 違うクラスを発見 32行目 A: 同じクラスが続いている 43行目 A: 同じクラスが続いている 54行目 A: 同じクラスが続いている 65行目 B: 違うクラスを発見 76行目 B: 同じクラスが続いている 87行目 C: 違うクラスを発見 97人分のデータを処理 10C>

あくまで一例です。質問者さんが言う「退避先」は上記コード例ではString classNameとしていますが、クラス名はファイル中では1文字で入っているのでchar型でもできますし、応用編としてMapを使えばクラスが連続していなくても集計できるようになります。

投稿2021/05/08 02:07

編集2021/05/08 02:10
dodox86

総合スコア9183

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

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

dodox86

2021/05/08 02:18

この手の「ファイルからデータ読み込み~グループ集計」の問題・課題は、Javaに限らず頻出のようです。teratailのトップページから「Java 集計」などとキーワード検索するとまた参考になる質問回答が得られるかもしれません。
javamurisugiru

2021/05/08 03:43

回答ありがとうございます!大変参考になりました。 同じように動かしてみて、クラス名が変わったときにきちんと処理が行われていることを確認し感動したのですが、「String className = "";」で何も入れず初期化しているのにもかかわらず、その後のif文でなぜclassName.equals(data[0])で比較できるのかがいまいち分かりませんでした。いつclassNameに値が入っているのでしょうか、、?
dodox86

2021/05/08 03:52

> いつclassNameに値が入っているのでしょうか、、? 「String className = "";」で値を入れています。空文字列(文字数が0の文字列)として入っています。それを初期値としている訳です。 ちなみにString className = null;だと if (className.equals(data[0])) の部分で java.lang.NullPointerException 例外が発生してしまいますが、 ifでの聞き方を変えて、if (data[0].equals(className)) { とすれば、初期値nullでもいけます。
dodox86

2021/05/08 03:57

> 「String className = "";」で値を入れています。空文字列(文字数が0の文字列)として入っています。それを初期値としている訳です。 一応、Javaとして正確に書くと、Stringクラス型の変数なので、値(文字列)そのものが入っている訳ではありません。Stringクラス型のインスタンスの実体への「参照」が入っています。""で言うと、空文字列の実体への「参照」です。nullは、その参照すらセットしていない状態です。
javamurisugiru

2021/05/08 04:26

詳しくありがとうございます! 私が勘違いをしていて、初めからif文のtrueに入ると思ってしまっていたのですが、String classNameには初め空文字列が入っていて、初めのレコードを読み込んだ時にクラス名「A」と比較し、異なっているためelseとして処理され、classNameに「A」が代入されるということですよね? この解釈であっていれば、理解できたと思います! とても詳しくて分かりやすい説明をありがとうございます><!
dodox86

2021/05/08 04:31

> 初めからif文のtrueに入ると思ってしまっていたのですが、String classNameには初め空文字列が入っていて、初めのレコードを読み込んだ時にクラス名「A」と比較し、異なっているためe lseとして処理され、classNameに「A」が代入されるということですよね? はい。その通りです。ですので、サンプルコードでは1行目で必ず「違うクラスを発見」となります。実用では1行目、グループの初回の検出では集計はしない(<前のグループが無いので)ようにする必要があるかもですね。
javamurisugiru

2021/05/08 04:39

そうですよね、まさに今初回の処理を飛ばさずに処理を行ってしまったため期待した結果が得られずにいました(笑) 初回のみ集計を飛ばす方法を考えてプログラムを完成させることができるよう頑張ってみます!本当にありがとうございました!
javamurisugiru

2021/05/08 06:34

すみません、見てくださっているかわからないのですが追加で質問してもよろしいでしょうか、、。 プログラムを作っていて、レコードのクラス名がclassNameと一致すれば得点を合計し、一致しなければ平均点を算出し表示するという仕組みで作っています。 最初のレコードの時はカウントを飛ばす処理はできたのですが、最後のクラスに入ったときに次に比べるレコードがないので結果を表示できなくなってしまいました。 if文で「最後のレコードの場合は平均点を算出し表示する」という処理を書こうと思ったのですが、この「最後のレコード(レコードの終わり?)」の書き方が分かりませんでした。これはどのように表記したらよいのでしょうか。もしお時間ありましたら教えていただけますと幸いです、、。
dodox86

2021/05/08 06:47 編集

> これはどのように表記したらよいのでしょうか。もしお時間ありましたら教えていただけますと幸いです、、。 そこはご自身で考えるべきな気もするのですが、、、プログラムの制御構造(流れ)の問題です。 (1) while()ループを抜けた後、出力していないクラスのデータがあれば追加で出力するとか、 (2) ループの条件を工夫して while (true) { line = reader.readLine(); if (line == null) { // データが終わりなので未出力のデータを出力した後、break break; } /* データ有りの場合の処理 */ } なかんじにするとか、でしょうか。
javamurisugiru

2021/05/08 07:05

回答ありがとうございます。 そうですよね、、自分でやるべきです、すみません>< if(line == null)などではやってみていたのですがやり方が違うのか思った通りに動かず苦戦していました。ですが考え方は合っているのですかね、、?もう少し頑張ります! お時間取らせてしまい本当にすみません、とても助かりました!
guest

0

Map 使ってみた。
「クラス名が変わったとき~」を判定して云々やるより素直だし、
クラス名でソートされてなくてもかまわんし。

Java

1import java.io.*; 2import java.util.*; 3 4class Foo { 5 6 public static void main(String args[]) throws Exception { 7 8 File file = new File("test.txt"); 9 FileReader fileReader = new FileReader(file); 10 BufferedReader bufferedReader = new BufferedReader(fileReader); 11 12 Map<String,int[]> map = new TreeMap<String,int[]>(); 13 14 String line; 15 while((line = bufferedReader.readLine()) != null) { 16 17 String[] data = line.split(" +"); 18 19 // int[4] score : 英語,数学,国語,カウント 20 int[] score = new int[] { Integer.parseInt(data[3]), Integer.parseInt(data[4]), Integer.parseInt(data[5]), 1 }; 21 int[] sum = map.get(data[0]); 22 if ( sum == null ) { 23 map.put(data[0], score); 24 } else { 25 sum[0] += score[0]; 26 sum[1] += score[1]; 27 sum[2] += score[2]; 28 sum[3] += score[3]; 29 } 30 } 31 bufferedReader.close(); 32 33 for ( String item : map.keySet() ) { 34 int[] sum = map.get(item); 35 System.out.printf("%s : %d %d %d\n", item, sum[0]/sum[3], sum[1]/sum[3], sum[2]/sum[3]); 36 } 37 } 38 39}

投稿2021/05/08 04:17

episteme

総合スコア16614

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

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

javamurisugiru

2021/05/08 04:36

回答ありがとうございます! いろいろ調べていた時にMapというクラス?があるのは見たのですが使い方がわからず断念しました、、。しかし今回私のデータを用いてコードを書いてくださっているおかげで少し理解できました! データが項目でソートされていなくても使えるのはとても便利ですね。 もう少し技術が伴ったら使えるようになりたいと思います。本当にありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問