Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

javaのマッチングについて

javaのマッチングについて教えてください。
今までcobolのマッチング処理しか組んだ事がなかった為
javaで組んでは見たもののcobolチックなってしまいました。
ソースは以下の通りです。
二つのキーを段階的に比較して処理を行っています。
質問1:片方のファイルの読み込みが終了した場合、cobolではキー部分にHIGH-VALUEを設定するのが普通ですが、JAVAではどうするんでしょうか?

質問2:ファイルの読み込みは、BufferedReaderクラスのreadLine()メソッドを使用し一レコードずつ読み込んでます。
書き込みには、BufferedWriterクラスのwrite()メソッド。改行用としてnewLine()メソッドを使用していますが、これって一般的なファイルの入出力方法でしょうか?

質問3:cobolのように処理を上からつらつら記述してしまいましたが、オブジェクト指向的な観点から見て、メソッド分割等。
java的なソースにするにはどのように改良したらよいでしょうか?

import java.io.*;

public class MatchTest {
    
    //変数定義
    static final String filename1 = "test1.txt";
    static final String filename2 = "test2.txt";
    static final String filename3 = "test3.txt";
    
    public static void main(String[] args) {
        
        try{
            
            //インスタンス生成
            FileReader file1  = new FileReader(filename1);
            FileReader file2  = new FileReader(filename2);
            FileWriter file3  = new FileWriter(filename3);
            BufferedReader br1 = new BufferedReader(file1);
            BufferedReader br2 = new BufferedReader(file2);
            BufferedWriter bw = new BufferedWriter(file3);

            
            //ヘッダレコード作成
            bw.write("header");
            System.out.println("header");
            bw.newLine();
            
            
            //データレコード作成変数初期化
            String ftr1SbtCd = null;
            String ftr2SbtCd = null;
            String ftr1LogInID = null;
            String ftr2LogInID = null;
            String ftr1Sonota = null;
            String ftr2Sonota = null;
            
            //一件READ
            String f1r = br1.readLine();
            String f2r = br2.readLine();
            
            
            //マッチング処理: ファイル1(前日)、ファイル2(当日)が共にEOFになるまで処理を行う。
            while (!(f1r ==null && f2r ==null)) {
                
                //比較項目取得
                if(f1r==null){
                    //ファイル1読み込み終了時、キーにhigh-valueを設定
                    ftr1SbtCd = "zzz";
                    ftr1LogInID = "999";
                    
                    ftr2SbtCd = f2r.substring(3,6);                        
                    ftr2LogInID = f2r.substring(6,9);                        
                }else if(f2r==null){
                    //ファイル2読み込み終了時、キーにhigh-valueを設定
                    ftr2SbtCd = "zzz";
                    ftr2LogInID = "999";            
                    
                    ftr1SbtCd = f1r.substring(3,6);
                    ftr1LogInID = f1r.substring(6,9);
                }else{
                    //本人識別ID取得
                    ftr1SbtCd = f1r.substring(3,6);
                    ftr2SbtCd = f2r.substring(3,6);                        
                    
                    //業務ログインID取得
                    ftr1LogInID = f1r.substring(6,9);
                    ftr2LogInID = f2r.substring(6,9);
                    
                    //その他項目取得
                    ftr1Sonota = f1r.substring(0,3) + f1r.substring(9,14); 
                    ftr2Sonota = f2r.substring(0,3) + f2r.substring(9,14); 
                }
                
                //比較(本人識別ID)
                int matchRs = ftr1SbtCd.compareTo(ftr2SbtCd);    
                
                if (matchRs < 0) {
                    System.out.println("削除:" + ftr1SbtCd + ftr1LogInID);
                    bw.write("削除:" + ftr1SbtCd + ftr1LogInID);
                    bw.newLine();
                    f1r = br1.readLine();
                } else if (matchRs > 0) {
                    System.out.println("追加:" + ftr2SbtCd + ftr2LogInID);
                    bw.write("追加:" + ftr2SbtCd + ftr2LogInID);
                    bw.newLine();
                    f2r = br2.readLine();
                } else {
                    //比較(業務ログインID)
                    matchRs = ftr1LogInID.compareTo(ftr2LogInID);
                    
                    if (matchRs < 0) {
                        System.out.println("削除:" + ftr1SbtCd + ftr1LogInID);
                        bw.write("削除:" + ftr1SbtCd + ftr1LogInID);
                        bw.newLine();
                        f1r = br1.readLine();
                    } else if (matchRs > 0) {
                        System.out.println("追加:" + ftr2SbtCd + ftr2LogInID);
                        bw.write("追加:" + ftr2SbtCd + ftr2LogInID);
                        bw.newLine();
                        f2r = br2.readLine();
                    } else {
                        //比較(その他項目)
                        if(ftr1Sonota.equals(ftr2Sonota)){
                            System.out.println("一致:" + ftr1SbtCd + ftr1LogInID);
                        }else{
                            System.out.println("更新:" + ftr2SbtCd + ftr2LogInID + ftr2Sonota);
                            bw.write("更新:" + ftr2SbtCd + ftr2LogInID + ftr2Sonota);
                            bw.newLine();
                        }
                        f1r = br1.readLine();
                        f2r = br2.readLine();                    
                    }
                }
            }
            
            //トレーラレコード作成
            bw.write("traler");
            System.out.println("traler");
            bw.newLine();
            
            //ファイル出力
            bw.flush();
            
            br1.close();
            br2.close();
            bw.close();
            
        }catch(Exception e){
            System.out.println("エラーだよ");
        }
    }
}

9

回答

11552

閲覧

9件の回答

評価

0

質問1:
これは、出力ファイル側の仕様によるんじゃないでしょうか?
自分で仕様を決めれるなら、固定長だし慣れている形でいいんじゃないでしょうか?(汗)

質問2:
そうですね。
ここのページの「入出力」の所に詳しく載っていますよ。

質問3:
ざっと見ただけなんですが、
cobolでは同じ形式のデータ取得時に
String ftr1SbtCd = null;
String ftr2SbtCd = null;


みたいな変数の使い方をしますが、
Javaでは、データ取得用オブジェクトを作って、
インスタンス変数名が異なるような作りになります。
FtrData ftrData1 = new FtrData();
FtrData ftrData2 = new FtrData();

FtrDataオブジェクトのデータは、ファイルの1行の文字列から作られるのが判っているので、
データの格納はFtrDataオブジェクトに
public void setData( String fr ) {
  ここでsubstringする
}
みたいな感じで作れますよね。
すると、データの格納が
ftrData1.setData( fr );
ftrData2.setData( fr );
だけでできちゃいますよね。

評価

0

>インスタンス変数名
インスタンスの変数名
です。(汗)
全然意味違うし!

評価

0

私もかつて、毎日のようにCOBOLでマッチング処理を書いてたもので、気持ちは分かります。

そうですね…。

質問1:eofに達した時点でひとつ目のループを抜けて、再度片方だけつらつらと出力するループを作るほうが、効率が良いでしょう。

質問2:ファイル入出力としては一般的です。

質問3:私がやるのなら、master用のクラス、transaction用のクラスを作って、読み込みとキー分け、マッチングはそちらに任せるように作ると思います(つまり、Readerクラスとキーを隠蔽します)。
出力用のクラスも作るかも。

以下、javaのポイントになります。

1. フィールドにもpublic/privateをつけましょう。

2. finalで宣言する場合は、全部大文字の変数名が良いです。
あと、連番は意味が分からなくなるんでやめたほうが良いです。

3. これは間違えてる人が本当に多いんですが…。
close処理は、finally句で行ってください。

評価

0

ひろちゃんさん。mioさんレスありがとうございます。

少し改良してみました。

以下ソースです。
package test.example;

import java.io.*;

public class MatchTest {
    
    //変数定義
    private static final String FILENAME1 = "master.txt";
    private static final String FILENAME2 = "trn.txt";
    private static final String FILENAME3 = "result.txt";
    
    public static void main(String[] args) {
        
        
        try{
            //インスタンス生成
            FileReader file1  = new FileReader(FILENAME1);
            FileReader file2  = new FileReader(FILENAME2);
            FileWriter file3  = new FileWriter(FILENAME3);
            DataGet mstDg = new DataGet();
            DataGet trnDg = new DataGet();
            
            BufferedReader br1 = new BufferedReader(file1);
            BufferedReader br2 = new BufferedReader(file2);
            BufferedWriter bw = new BufferedWriter(file3);
            

            
            //ヘッダレコード作成
            bw.write("header");
            System.out.println("header");
            bw.newLine();
            
            
            //一件READ
            String mst = br1.readLine();
            String trn = br2.readLine();
            
            
            //マッチング処理: ファイル1(前日)、ファイル2(当日)が共にEOFになるまで処理を行う。
            while (!(mst ==null && trn ==null)) {
                
                mstDg.setSbtCd(mst);
                mstDg.setLogInID(mst);                        
                mstDg.setSonota(mst); 
                trnDg.setSbtCd(trn);
                trnDg.setLogInID(trn);                        
                trnDg.setSonota(trn); 
                
                //比較(本人識別ID)
                int matchRs = mstDg.getSbtCd().compareTo(trnDg.getLogInID());    
                
                if (matchRs < 0) {
                    System.out.println("削除:" + mstDg.getSbtCd() + mstDg.getLogInID());
                    bw.write("削除:" + mstDg.getSbtCd() + mstDg.getLogInID());
                    bw.newLine();
                    mst = br1.readLine();
                } else if (matchRs > 0) {
                    System.out.println("追加:" + trnDg.getSbtCd() + trnDg.getLogInID());
                    bw.write("追加:" + trnDg.getSbtCd() + trnDg.getLogInID());
                    bw.newLine();
                    trn = br2.readLine();
                } else {
                    //比較(業務ログインID)
                    matchRs = mstDg.getLogInID().compareTo(trnDg.getLogInID());
                    
                    if (matchRs < 0) {
                        System.out.println("削除:" + mstDg.getSbtCd() + mstDg.getLogInID());
                        bw.write("削除:" + mstDg.getSbtCd() +  mstDg.getLogInID());
                        bw.newLine();
                        mst = br1.readLine();
                    } else if (matchRs > 0) {
                        System.out.println("追加:" + trnDg.getSbtCd() + trnDg.getLogInID());
                        bw.write("追加:" + trnDg.getSbtCd() + trnDg.getLogInID());
                        bw.newLine();
                        trn = br2.readLine();
                    } else {
                        //比較(その他項目)
                        if(mstDg.getSonota().equals(trnDg.getSonota())){
                            System.out.println("一致:" + mstDg.getSbtCd() + mstDg.getLogInID());
                        }else{
                            System.out.println("更新:" + trnDg.getSbtCd() + trnDg.getLogInID() + trnDg.getSonota());
                            bw.write("更新:" + trnDg.getSbtCd() + trnDg.getLogInID() + trnDg.getSonota());
                            bw.newLine();
                        }
                        mst = br1.readLine();
                        trn = br2.readLine();                    
                    }
                }
            }
            
            //トレーラレコード作成
            bw.write("traler");
            System.out.println("traler");
            bw.newLine();
            
            //ファイル出力
            bw.flush();
            
            br1.close();
            br2.close();
            bw.close();            
            
        }catch(IOException e){
            System.out.println("エラーだよ");
        }finally{
        }
    }
}

***************************追加プログラム
package test.example;

public class DataGet {
    private String sbtCd = null;
    private String logInID = null;
    private String sonota = null;
    
    public String getLogInID() {
        return logInID;
    }
    public void setLogInID(String rec) {
        if(rec == null){
            this.logInID = "999";
        }else{
            this.logInID = rec.substring(6,9);            
        }
    }
    public String getSbtCd() {
        return sbtCd;
    }
    public void setSbtCd(String rec) {
        if(rec == null){
            this.sbtCd = "zzz";            
        }else{
            this.sbtCd = rec.substring(3,6);            
        }
    }
    public String getSonota() {
        return sonota;
    }
    public void setSonota(String rec) {
        if(rec == null){
            this.sbtCd = "";            
        }else{
            this.sbtCd = rec.substring(0,3) + rec.substring(9,14);            
        }
    }
    
}


ってな感じで、データ取得用オブジェクトを追加してみました。
ひろちゃんさんイメージとしてはこんな感じでしょうか?

mioさん
>質問3:私がやるのなら、master用のクラス、transaction用のク>ラスを作って、読み込みとキー分け、マッチングはそちらに任せ>るように作ると思います(つまり、Readerクラスとキーを隠蔽し>ます)。
>出力用のクラスも作るかも。
 すみません。イメージがわきません。
 分けた場合の記述をサンプル程度でいいので教えて頂くとありがたいです。

以下、javaのポイントになります。

>1. フィールドにもpublic/privateをつけましょう。
 private変数追加しました。

>2. finalで宣言する場合は、全部大文字の変数名が良いです。
>あと、連番は意味が分からなくなるんでやめたほうが良いです。
 ファイル名変更しました。

>3. これは間違えてる人が本当に多いんですが…。
>close処理は、finally句で行ってください。 
 初歩的な事かもですが、finally句にcloseを入れると
 スコープエラーになってしまいます。
 どのようにすればよろしいでしょうか?

評価

0

そんな感じです。

あと、なんで自分はこういう考え方ができないのだろうとか考えると地獄を見ます。(笑)
最初から自分だけで完璧なものはできません。確かにこっちの方が効率的だな、こういう風に考えればいいのか!と納得して吸収すれば必ずできるようになります。
最初は難しいと思いますが、そのうち脳が勝手にこれはオブジェクトに分離できないか!?とか考え出してきますので、あせらずにがんばってください。

評価

0

ひろちゃんさん。ありがとう^−^

評価

0

もう一度見てみたら、ちょっと違いました。(汗)

mstDg.setSbtCd(mst);
mstDg.setLogInID(mst);                        
mstDg.setSonota(mst); 
trnDg.setSbtCd(trn);
trnDg.setLogInID(trn);                        
trnDg.setSonota(trn); 

この部分は、mstDgもtrnDgも同じ事をやっていますね。
だから、
「ファイルから取得した固定長1行の文字列を与えれば、各フィールドにデータを振り分けてくれる」関数も作れますよね。

私は、これをDataGetクラスに作る前提でお話したのですが、本当はデータ用クラスが固定長の仕様のことを知っているのはおかしいので、固定長の仕様とデータのことを管理するクラスがあるのがBestですけど。
(こういう風にいろいろ考え出すと大変になってきます。(笑))

評価

0

>3. これは間違えてる人が本当に多いんですが…。
>close処理は、finally句で行ってください。 
 初歩的な事かもですが、finally句にcloseを入れると
 スコープエラーになってしまいます。
 どのようにすればよろしいでしょうか? 
-------------------------------------------------------
try〜catch内でオブジェクトを宣言しているからです。

try{
 //インスタンス生成
  FileReader file1  = new FileReader(FILENAME1);
上記の部分を

FileReader file1;
try{
 //インスタンス生成
  file1  = new FileReader(FILENAME1);
のようにし、finalyではnullチェックを入れれば問題ないかと。 

評価

0

一番肝心なことですが、DataGetという「処理を想定して必要なデータを持たせる」が、オブジェクト指向ではありません。
「レコード」「ファイル」という「モノ」を最初に考えて、それに付随する処理をつけるように考えます。

>すみません。イメージがわきません。
あくまでたとえば、ですが(動作確認もなにもしてないし)、こんなのを考えてみました。

import java.io.*;
public class DataFile {
    private BufferedReader br = null;
    private Field field = null;

    public DataFile(String fileName, Field field) throws FileNotFoundException {
        br = new BufferedReader(new FileReader(fileName));
        this.field = field;
    }
    public void read() throws IOException {
        field.setRecord(br);
    }
    public Field getField() {
        return field;
    }
    public void close() {
        try {
            br.close();
        } catch (IOException e) {}
    }
    public int match(Field f) {
        int comp = 0;
        String[] master = field.getKeys();
        String[] trans = f.getKeys();
        // ここで比較する
        return comp;
    }

    public interface Field {
        /** ファイルからレコードを取得して設定する */
        public void setRecord(BufferedReader br) throws IOException;
        /** レコード内容を文字列で返す */
        public String getRecord();
        /** キーフィールドの配列を返す */
        public String[] getKeys();
    }
}

最初にFieldを実装して、レコードの情報を教えます。
MatchTestでDataFileクラスを2つ用意して、master/transactionとしてファイル名とFieldの実装クラスを渡します。
どっちでもいいですが、getField()したものをmatch()を呼び出して、その結果に応じて出力します。
(別途matcherクラスを使うのであれば、インタフェースを変えたほうがいいかも知れませんが)

>ファイル名変更しました。
大文字にはなってますが、連番はそのままですね。
練習程度なら良いですが、仕事でこういうつけかたはやめましょう。
それから、sonotaというのも、例えばdataFieldとか英語で統一しましょう。
COBOLだと、区分をKBNなどなど、一般的でしたけどね。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。