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

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

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

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

Q&A

解決済

3回答

14505閲覧

Javaで、二つの入力ファイルを結合し一つの出力ファイルにする方法で躓いてます。

churi_yuzuki

総合スコア32

Java

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

0グッド

0クリップ

投稿2016/07/25 16:14

A00122222222200001000
A00133333333300000500
B00244444444400020000
B00255555555500003000

というテキストファイル(4桁組織番号、9桁会員番号、8桁値段)と、

A001222222222○○ ○○
A001333333333×× ××
B002444444444○× □○
B002666666666□□ □□

というテキストファイル(4桁組織番号、9桁会員番号、全角40文字名前)を読み込み、
「組織番号会員番号名前値段」
という様に、テキストファイルにして出力するメソッドを作成したいです。

TreeMapを使えば良いであろうことは分かるのですが、いまいち結合の方法が分かりません。
また、例外処理の記述方法も迷っているので良かったら教えて頂きたいです。

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

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

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

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

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

yuba

2016/07/25 16:25

レコードを識別する識別子(プライマリキー)は何ですか。 組織番号? 会員番号? それとも両方?
yuba

2016/07/25 16:27

また、レコードの順番に意味はありますか? それと、ファイルはメモリに収まるほどのサイズ(数GB以内)ですか?
churi_yuzuki

2016/07/25 22:30

主キーは組織番号と会員番号の両方だと思われます。 レコードは組織番号、会員番号の昇順にソートされています。 どちらも想定データ量は10000レコードとなっています。
yuba

2016/07/25 23:01

「思われます」ということは、もしかしてこの質問文以上の指示はもらわずにプログラムを作ろうとしていますか? では、組織番号と会員番号をひとまとめのレコードIDと呼ぶことにして、ファイルAにはあるがBにはないレコードID、もしくはその逆があったときにはどうなるのが正しいですか? また、AやBに重複したレコードがあった場合にはどうなるのが正しいですか? そして大事なことかもしれないのですが、これは今後ずっと使うプログラムですか、それとも一度使うためのプログラムですか?(一度使うだけなら、ExcelかUNIXコマンドでやったほうが早そうですし)
churi_yuzuki

2016/07/26 00:31

言葉足らずで申し訳ありません。 前者のファイルにはあるが後者にない場合、名前の部分を「該当者なし」と表示、その逆であれば値段の部分を00000000として表示させたいです。 ソートされている前提なので同じファイル内で重複はないです。 課題で与えられた問題なので一回しか使いません。
yuba

2016/07/26 00:36

あ、仕事ではなく課題ですか。では楽しんでいきましょう。お昼休みにでも考え方を説明します。
guest

回答3

0

ベストアンサー

この課題、出題者はおそらく「シーケンシャル処理」という隠れた課題を設定しています。まずそれが何のことだか説明します。

レコード数はどちらのファイルも10000件が上限とのこと。だから余裕でPCのメモリに収まります。そうしたらお考えの通りTreeMapを使ったり、単に配列に突っ込んだりするだけでも処理ができます。

ただ、入力ファイルが50GBくらいあったとしたら、メモリ上ではとても処理できません。ディスク上にデータを置いたまま処理しないといけなくなります。そしてディスク上のテキストファイルというのは不便なもので、「○○行目」という指定でアクセスすることができず、先頭から順に読んでいくことしかできません。実際の業務データならこういうケースはあることなんです。
そこで
●限られたメモリで
●先頭から順に読むことしかできないファイルを
●できれば一巡、そうでなくても少ない回数なめるだけで
処理するというプログラミングが要求されてくるのです。

この課題の面白いところ、それはファイル全体を読み込むことなく一行ずつ読みながら一巡で解けるようになっているところです。ポイントは、ID順にソートされていること。ですから、そういう風に処理するプログラムが提出できたら、出題者が「こいつわかってるな」とにやにやすること請け合いです。

解法の考え方行きます。

ファイルAとファイルBを開いて、先頭の行同士を見比べます。先頭の行同士のID(前から13桁ですね)を見比べるのです。
●これらがA/B一致していたら、一致レコード発見ですから合体させたレコードを出力ファイルに書き出して、AとBは次の行を読みましょう。
●A/BのIDが一致していなかったら、辞書順でどちらが若いかを調べます。
▶Aが若いなら、ファイルBにはそれに該当するレコードはないと断言できます(Bはソートされているのですから)。ID-Aは迷子の「値段」であると判断でき、名前に「該当者なし」と書いたレコードを出力します。Aファイルだけ次の行に読み進みます。
▶Bが若いのなら、ファイルAにはそれに該当するレコードはない(以下略)。値段に00000000と書いたレコードを出力し、Bファイルだけ次の行に読み進みます。
●ファイルAかファイルBのどちらかがすでに終端に達してしまっていたら、残った方のファイルの残りレコードはすべて迷子です。そのように出力していきます。
●ファイルAもファイルBも終端に達したら処理は終了です。出力ファイルを閉じてプログラムを抜けます。

さて、これでプログラムは書けそうでしょうか。
無理そうだったら言ってください。

投稿2016/07/26 03:19

yuba

総合スコア5568

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

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

churi_yuzuki

2016/07/27 04:06 編集

ご丁寧にありがとうございます! 頂いたヒントを元にコードを書いてみたのですが、、、 try{ while(lineA != null && lineB != null){ if(lineA == null){ wr.write(codeB); wr.write(name); wr.write("00000000"); wr.newLine(); wr.flush(); lineB = rdB.readLine(); }else if(lineB == null){ wr.write(codeA); wr.write("<該当者なし>"); wr.write(exp); wr.newLine(); wr.flush(); lineA = rdA.readLine(); }else{ if(codeA.equals(codeB)){ wr.write(codeA); wr.write(name); wr.write(exp); wr.newLine(); wr.flush(); lineA = rdA.readLine(); lineB = rdB.readLine(); }else{ if(codeA.compareTo(codeB) < 0){ wr.write(codeA); wr.write("<>"); wr.write(exp); wr.newLine(); wr.flush(); lineA = rdA.readLine(); }else{ wr.write(codeB); wr.write(name); wr.write("00000000"); wr.newLine(); wr.flush(); lineB = rdB.readLine(); } } } } } catch (IOException e) { e.printStackTrace(); } catch (RuntimeException e) { e.printStackTrace(); System.out.println("システムエラー"); } finally { try { if (wr != null) { wr.close(); } if (rdB != null) { rdB.close(); } if (rdA != null) { rdA.close(); } } catch (IOException e) { e.printStackTrace(); } } ``` 最初のif分がデッドコードになってしまいました。 また、例外処理も自信ないです。。。
churi_yuzuki

2016/07/26 08:44

すいません、マークダウン?出来てませんでした。
yuba

2016/07/26 09:06

lineA, lineBをnull判定していく条件分岐がいろいろ間違っているのとか、codeA, expあたりの変数が更新されていないのとか直せば動くようになるんじゃないでしょうか。 例外処理は、古い書き方ですがおおむね正しいです。ただ、closeするの3つを一緒にtryブロックに入れちゃうのはよくありません。(一個目のcloseが例外吐いたら、2つめの3つめのcloseが実行されずに飛ばされてしまいます)
yuba

2016/07/26 09:22

条件分岐が間違っているのは最初のwhileだけか。ここは && じゃなくて || ですね。
yuba

2016/07/26 09:23

そして、codeA, name, codeB, expを読み出すところをwhile文の内側に持ってくれば、たぶん完成です。
churi_yuzuki

2016/07/27 01:20

指摘して頂いた点を色々訂正してみたらなんとか正しいアウトプットを得ることが出来ました! とても丁寧にご指導してくださりありがとうございました!
guest

0

実装されたソースコードがないので例外ハンドリングについてはコメントできませんが…おそらく実施されたいことは以下のようなことなのではないかと。

java

1public class TwoFileMerge { 2 /** 3 * A00122222222200001000のデータを4桁組織番号、9桁会員番号、8桁値段に分割する。 4 * @param data 5 * @return 6 */ 7 protected Map<String,Object> separateDataA(String data) { 8 String orgCode = data.substring(0,4); 9 String memberCode = data.substring(4,13); 10 String price = data.substring(13); 11 12 Map<String, Object> map = new HashMap<>(); 13 map.put("orgCode", orgCode); 14 map.put("memberCode", memberCode); 15 map.put("price", price); 16 17 return map; 18 } 19 20 /** 21 * A001222222222○○ ○○のデータを4桁組織番号、9桁会員番号、全角40文字名前に分割する。 22 * @param data 23 * @return 24 */ 25 protected Map<String,Object> separateDataB(String data) { 26 String orgCode = data.substring(0,4); 27 String memberCode = data.substring(4,13); 28 String name = data.substring(13); 29 30 Map<String, Object> map = new HashMap<>(); 31 map.put("orgCode", orgCode); 32 map.put("memberCode", memberCode); 33 map.put("name", name); 34 35 return map; 36 } 37 38 /** 39 * 2つのデータからマージした結果を返す。 40 * @param dataA データA 41 * @param dataB データB 42 * @return マージ後の結果 43 */ 44 public Map<String, Object> mergeData(String dataA, String dataB) { 45 Map<String, Object> mapA = separateDataA(dataA); 46 Map<String, Object> mapB = separateDataB(dataB); 47 48 mapA.putAll(mapB); 49 50 return mapA; 51 } 52}

検証コードは以下:

java

1import static org.junit.Assert.*; 2 3import java.util.Map; 4 5import org.junit.Before; 6import org.junit.Test; 7 8public class TwoFileMergeTest { 9 10 TwoFileMerge merge = null; 11 12 @Before 13 public void setUp() throws Exception { 14 merge = new TwoFileMerge(); 15 } 16 17 @Test 18 public void test() { 19 String dataA = "A00122222222200001000"; 20 String dataB = "A001222222222○○ ○○"; 21 22 Map<String, Object> result = merge.mergeData(dataA, dataB); 23 24 // Mapの中身がマージされた結果かを確認 25 assertEquals(result.containsKey("orgCode"), true); 26 assertEquals(result.containsKey("memberCode"), true); 27 assertEquals(result.containsKey("name"), true); 28 assertEquals(result.containsKey("price"), true); 29 30 // 値のチェック 31 assertEquals("A001" ,result.get("orgCode")); 32 assertEquals("222222222" , result.get("memberCode")); 33 assertEquals("○○ ○○", result.get("name")); 34 assertEquals("00001000", result.get("price")); 35 } 36 37} 38

投稿2016/07/25 17:40

A-pZ

総合スコア12011

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

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

churi_yuzuki

2016/07/27 01:23

情報が少ない中ご回答ありがとうございます。 Mapを使ったコーディングとして参考にさせて頂きます!
guest

0

基本的にはそれらの情報(組織番号会員番号名前値段)をひとまとめにしたクラスを作成し、会員番号をキーにしてMapを作成し(おそらくデータごとに確実にユニークと思われるため)、1つ目のファイルを読み込んでデータを作成し、2つ目のファイルを会員番号をキーにしてオブジェクトを引き出し、そのオブジェクトに名前のデータを追加する、という形式になるのでは?
例外発生条件がわかりませんが、組織番号と会員番号が一致しないものが見つかった時、でしょうか?

投稿2016/07/25 16:43

swordone

総合スコア20651

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

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

churi_yuzuki

2016/07/27 01:24

キー値を元にオブジェクトを引き出すというイメージがまだついていないので1からコレクションの範囲を復習しようと思います。 ご回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問