读写文件处理的例子(避免outofmemory的方法)

来源:互联网 发布:deepin linux开3d特效 编辑:程序博客网 时间:2024/06/06 00:14

写了一个小工具,遇到38G的大数据文件,出outofmemory错误,崩溃了。

改写了一下,没出错,效率还挺高,留作纪念。经验只有一条,千万不要把数据都读到内存里,否则神木也救不了。

打包用了fat jar插件,插到包成,超赞。

/**
 * 機能:雇用保険のインタフェースファイルを統合統計用の格式に変換 <br>
 * 入力ファイル:適用統計台帳ファイル <br>
 * 出力ファイル:適用統計台帳事業所情報ファイル と 適用統計台帳被保険者情報ファイル
 *
 * @author btxiajunqing
 *
 */
public class IFHenKan {

    // ログ
    private final static Logger log = Logger.getLogger(IFHenKan.class);
    // 入力ファイル
    private File inputFile = null;
    // 一時ファイル名
    private File tmpFile = null;
    // カレンダー対象
    private Calendar cpcalendar = new GregorianCalendar();
    // データフォーマット
    private SimpleDateFormat parseTime = new SimpleDateFormat("yyyyMMdd");
    // 処理日を取得
    private Calendar currentDate = null;

    /**
     * 適用統計台帳ファイルの変換を実行する
     *
     * @param file
     *            入力ファイル
     * @return 正常終了の場合:true 異常終了の場合:false
     */
    public boolean execute(File file) {

        // 入力データを取得
        inputFile = file;

        try {
                    
            // 台帳のデータをUTF-16BEで122バイト毎に読み込んで、出力チェックしてから一時出力ファイルに書き込み
            createTempFile();
            // 一時出力ファイルから一行づつデータを読み込み、解析して事業所情報ファイルと被保険者情報ファイルに出力
            readFileByChars();
            //一時出力ファイルを削除する
            delTmpFile();

        } catch (Exception e) {
            log.error(e);
            return false;
        }

        return true;

    }

    /**
     * 一時出力ファイルを一行づつ読み込み、解析して結果ファイルを作成します。 <br>
     * 入力ファイル:一時出力ファイル <br>
     * 出力ファイル:適用統計台帳事業所情報ファイル と 適用統計台帳被保険者情報ファイル
     *
     * @throws Exception
     */
    public void readFileByChars() throws Exception {

        // 事業所情報ファイル
        File file_jigyosyo = null;
        // 被保険者情報ファイル
        File file_hihokensha = null;

        // 読み込みファイルのオブジェクト
        BufferedReader reader = null;
        // 事業所情報ファイルを書き込む用ストリーム
        BufferedWriter writer_jigyosyo = null;
        // 被保険者情報ファイルを書き込む用ストリーム
        BufferedWriter writer_hihokensha = null;
        // 事業所情報の項目の配列
        String[] arr_output_jigyosyo = new String[Const.CNT_JIGYOSYO];
        // 被保険者情報の項目の配列
        String[] arr_output_hihokensha = new String[Const.CNT_HIHOKENSHA];

        // 一レコードを格納用オブジェクト
        String line = "";
        StringBuilder temp = new StringBuilder(Const.LEN_RECORD_TEKIYO);
        // 事業所情報件数
        long cnt_jigyosyo = 0L;
        // 被保険者情報件数
        long cnt_hihokensha = 0L;

        try {

            // 事業所情報ファイルのフルパスを取得
            file_jigyosyo = new File(inputFile.getParent() + File.separator
                    + Const.FILE_JIGYOSHO);
            // 被保険者情報のフルパスを取得
            file_hihokensha = new File(inputFile.getParent() + File.separator
                    + Const.FILE_HIHOKENSHA);

            // 事業所情報ファイルを作成
            file_jigyosyo.createNewFile();
            // 被保険者情報ファイルを作成
            file_hihokensha.createNewFile();

            // 入力ファイルを読み込み
            reader = new BufferedReader(new InputStreamReader(
                    new FileInputStream(tmpFile), "UTF-8"));
            // 事業所情報ファイル
            writer_jigyosyo = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(file_jigyosyo), "UTF-8"));
            // 被保険者情報ファイル
            writer_hihokensha = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(file_hihokensha), "UTF-8"));

            // 一行づづ読み込み、項目を解析する
            while ((line = reader.readLine()) != null) {

                // レコードのオブジェクトを初期化
                if (temp.length() != 0) {
                    temp.delete(0, temp.length());
                }

                // レコードのデータを設定
                temp.append(line);

                // 事業所情報の場合(レコード種別コード=0100)
                if (Const.RECORD_TYPE_JIGYOSYO.equals(temp.substring(28, 32))) {

                    // 事業所番号
                    int start = 0;
                    int end = start + Const.LEN_ITEM_10;
                    arr_output_jigyosyo[0] = format(temp.substring(start, end),
                            Const.LEN_ITEM_10);

                    // 設置処理日
                    start = end + Const.LEN_ITEM_10 + Const.LEN_ITEM_8
                            + Const.LEN_ITEM_4 + Const.LEN_ITEM_8;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_jigyosyo[1] = format(temp.substring(start, end),
                            Const.LEN_ITEM_8);

                    // 事業所管轄
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_jigyosyo[2] = format(temp.substring(start, end),
                            Const.LEN_ITEM_1);

                    // 廃止処理日
                    start = end + Const.LEN_ITEM_2 + Const.LEN_ITEM_1
                            + Const.LEN_ITEM_1 + Const.LEN_ITEM_1
                            + Const.LEN_ITEM_2 + Const.LEN_ITEM_8;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_jigyosyo[3] = format(temp.substring(start, end),
                            Const.LEN_ITEM_8);

                    // 事業所区分変更訂正処理日
                    start = end;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_jigyosyo[4] = format(temp.substring(start, end),
                            Const.LEN_ITEM_8);

                    // 移転日
                    start = end;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_jigyosyo[5] = format(temp.substring(start, end),
                            Const.LEN_ITEM_8);

                    // 移転併合先事業所番号
                    start = end;
                    end = start + Const.LEN_ITEM_10;
                    arr_output_jigyosyo[6] = format(temp.substring(start, end),
                            Const.LEN_ITEM_10);

                    // 事業所情報リストのデータを出力する
                    for (int j = 0; j < arr_output_jigyosyo.length; j++) {
                        writer_jigyosyo.write("\"" + arr_output_jigyosyo[j]
                                + "\"");

                        // 最後の項目に改行を付ける
                        if (j == arr_output_jigyosyo.length - 1) {
                            writer_jigyosyo.write("\n");
                            // ほかの項目にカンマを付ける
                        } else {
                            writer_jigyosyo.write(",");
                        }
                    }

                    // 事業所情報件数を合算
                    cnt_jigyosyo++;

                    // 被保険者情報の場合(レコード種別コード=0200)
                } else if (Const.RECORD_TYPE_HIHOKENSHA.equals(temp.substring(
                        28, 32))) {

                    // 事業所番号
                    int start = 0;
                    int end = start + Const.LEN_ITEM_10;
                    arr_output_hihokensha[0] = format(temp
                            .substring(start, end), Const.LEN_ITEM_10);

                    // 被保険者番号
                    start = end;
                    end = start + Const.LEN_ITEM_10;
                    arr_output_hihokensha[1] = format(temp
                            .substring(start, end), Const.LEN_ITEM_10);

                    // 取得日
                    start = end;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_hihokensha[2] = format(temp
                            .substring(start, end), Const.LEN_ITEM_8);

                    // 取得処理日
                    start = end + Const.LEN_ITEM_4;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_hihokensha[3] = format(temp
                            .substring(start, end), Const.LEN_ITEM_8);

                    // 生年月日
                    start = end;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_hihokensha[4] = format(temp
                            .substring(start, end), Const.LEN_ITEM_8);

                    // 取得時被保険者種類
                    start = end;
                    end = start + Const.LEN_ITEM_2;
                    arr_output_hihokensha[5] = format(temp
                            .substring(start, end), Const.LEN_ITEM_2);

                    // 取得原因
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[6] = format(temp
                            .substring(start, end), Const.LEN_ITEM_1);

                    // 賃金態様
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[7] = format(temp
                            .substring(start, end), Const.LEN_ITEM_1);

                    // 職種
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[8] = format(temp
                            .substring(start, end), Const.LEN_ITEM_1);

                    // 賃金
                    start = end;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_hihokensha[9] = format(temp
                            .substring(start, end), Const.LEN_ITEM_8);

                    // 離職日
                    start = end;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_hihokensha[10] = format(temp.substring(start,
                            end), Const.LEN_ITEM_8);

                    // 離職処理日
                    start = end;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_hihokensha[11] = format(temp.substring(start,
                            end), Const.LEN_ITEM_8);

                    // 離職票初回交付日
                    start = end;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_hihokensha[12] = format(temp.substring(start,
                            end), Const.LEN_ITEM_8);

                    // 転入日
                    start = end;
                    end = start + Const.LEN_ITEM_8;
                    arr_output_hihokensha[13] = format(temp.substring(start,
                            end), Const.LEN_ITEM_8);

                    // 支給番号
                    start = end;
                    end = start + Const.LEN_ITEM_13;
                    arr_output_hihokensha[14] = format(temp.substring(start,
                            end), Const.LEN_ITEM_13);

                    // 喪失原因
                    start = end;
                    end = start + Const.LEN_ITEM_2;
                    if (Const.REASON_SOUSHITSI.equals(temp
                            .substring(start, end))) {
                        arr_output_hihokensha[15] = " ";
                    } else {
                        arr_output_hihokensha[15] = temp.substring(start + 1,
                                end);
                    }

                    // 争い被処分フラグ
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[16] = format(temp.substring(start,
                            end), Const.LEN_ITEM_1);

                    // 争い被解雇フラグ
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[17] = format(temp.substring(start,
                            end), Const.LEN_ITEM_1);

                    // 争い支処分フラグ
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[18] = format(temp.substring(start,
                            end), Const.LEN_ITEM_1);

                    // 争い支解雇フラグ
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[19] = format(temp.substring(start,
                            end), Const.LEN_ITEM_1);

                    // 性別
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[20] = format(temp.substring(start,
                            end), Const.LEN_ITEM_1);

                    // 離職票交付フラグ
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[21] = format(temp.substring(start,
                            end), Const.LEN_ITEM_1);

                    // 離職時被保険者種類
                    start = end;
                    end = start + Const.LEN_ITEM_2;
                    arr_output_hihokensha[22] = format(temp.substring(start,
                            end), Const.LEN_ITEM_2);

                    // 雇用形態
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[23] = format(temp.substring(start,
                            end), Const.LEN_ITEM_1);

                    // 特例非該当フラグ
                    start = end;
                    end = start + Const.LEN_ITEM_1;
                    arr_output_hihokensha[24] = format(temp.substring(start,
                            end), Const.LEN_ITEM_1);

                    // 離職処理時算定基礎月数
                    start = end;
                    end = start + Const.LEN_ITEM_4;
                    arr_output_hihokensha[25] = format(temp.substring(start,
                            end), Const.LEN_ITEM_4);

                    // 被保険者情報リストのデータを出力する
                    for (int j = 0; j < arr_output_hihokensha.length; j++) {
                        writer_hihokensha.write("\"" + arr_output_hihokensha[j]
                                + "\"");

                        // 最後の項目に改行を付ける
                        if (j == arr_output_hihokensha.length - 1) {
                            writer_hihokensha.write("\n");
                            // ほかの項目にカンマを付ける
                        } else {
                            writer_hihokensha.write(",");
                        }
                    }

                    // 被保険者情報を合算
                    cnt_hihokensha++;
                }
            }

            // 処理した件数をログに出力
            log.info("事業所情報件数:" + cnt_jigyosyo);
            log.info("被保険者情報件数:" + cnt_hihokensha);

        } catch (Exception e1) {
            throw e1;
        } finally {
            // 読み込みのストリームを閉じる
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    throw e1;
                }
            }
            // 事業所情報を書き込みのストリームを閉じる
            if (writer_jigyosyo != null) {
                try {
                    writer_jigyosyo.flush();
                    writer_jigyosyo.close();
                } catch (IOException e1) {
                    throw e1;
                }
            }
            // 被保険者情報を書き込みのストリームを閉じる
            if (writer_hihokensha != null) {
                try {
                    writer_hihokensha.flush();
                    writer_hihokensha.close();
                } catch (IOException e1) {
                    throw e1;
                }
            }
        }
    }

    /**
     * データ出力判定を行う。 <br>
     * 離職日≠00000000 かつ 離職日 ≧ 処理日2年前 または <br>
     * 離職日=00000000 かつ 取得処理日 ≧ 処理日1年前の場合、出力trueを返却する。
     *
     * @param lossDate
     *            離職日
     * @param getDate
     *            取得処理日
     * @return
     * @throws Exception
     */
    public boolean compareDate(String lossDate, String getDate)
            throws Exception {

        // チェック用日付
        Date date = null;
        // チェック結果
        boolean result = false;

        // 離職日が空の場合、"00000000"に設定
        if (lossDate.trim().length() == 0) {
            lossDate = Const.DUMMY_DATE;
        }

        // 取得日が空の場合、"00000000"に設定
        if (getDate.trim().length() == 0) {
            getDate = Const.DUMMY_DATE;
        }

        try {

            // 離職日=00000000 かつ 取得日 ≧ 処理日1年前の場合
            if (Const.DUMMY_DATE.equals(lossDate)) {
                date = parseTime.parse(getDate);
                cpcalendar.setTime(date);

                // 処理日1年前の日付を取得
                cpcalendar.add(GregorianCalendar.YEAR, 1);

                // 取得日 ≧ 処理日1年前の場合,TRUEを設定、それ以外FALSEを設定
                if (cpcalendar.compareTo(currentDate) >= 0) {
                    result = true;
                } else {
                    result = false;
                }

                // 離職日≠00000000 かつ 離職日 ≧ 処理日2年前
            } else {
                date = parseTime.parse(lossDate);
                cpcalendar.setTime(date);

                // 処理日2年前の日付を取得
                cpcalendar.add(GregorianCalendar.YEAR, 2);

                // 離職日 ≧ 処理日2年前の場合、TRUEを設定、それ以外FALSEを設定
                if (cpcalendar.compareTo(currentDate) >= 0) {
                    result = true;
                } else {
                    result = false;
                }
            }

        } catch (ParseException e) {
            throw e;
        } catch (Exception e1) {
            throw e1;
        }

        return result;
    }

    /**
     * 解析した項目のタイプ編集を行う
     *
     * @param str
     *            項目
     * @param len
     *            指定される長さ
     * @return 編集後項目
     */
    public String format(String str, int len) {

        // 編集後項目
        String result = str;

        // 項目の長さは、指定される長さと違い場合、前に半角数字0を付ける
        if (null != str && len != str.trim().length()) {
            StringBuilder tmp = new StringBuilder();

            // 足りない桁に半角数字0を付ける
            for (int i = 0; i < len - str.trim().length(); i++) {
                tmp.append("0");
            }

            // 編集した項目を設定
            tmp.append(str.trim());
            result = tmp.toString();
        }

        return result;
    }

    /**
     * 台帳のデータをUTF16-BEのコーディング格式で122バイト毎に読み込んで、<br>
     * 出力チェックしてから一時出力ファイルに書き込み
     *
     * @throws Exception
     */
    public void createTempFile() throws Exception {

        // 122バイトの文字配列
        char[] tempchars = new char[Const.LEN_RECORD_TEKIYO];
        // 読み込み文字数
        int charread = 0;
        // 事業所情報出力フラグ
        boolean jigyosyo_flg = false;
        // 退避用事業所情報
        String jigyosyo_info = "";
        // 離職日
        String liShokuBi = "";
        // 取得処理日
        String shoTokuSholiBi = "";
        // ファイル読み込みストリーム
        BufferedReader reader = null;
        // ファイル書き込みストリーム
        BufferedWriter writer = null;
        // 一レコードのデータを格納用オブジェクト
        StringBuilder temp = new StringBuilder(Const.LEN_RECORD_TEKIYO);

        try {

            // 一時出力ファイル
            tmpFile = new File(inputFile.getParent() + File.separator
                    + Const.DAICHO_TMP_FILE);
            // 入力ファイルをUTF16-BEのコーディング格式で読み込み
            reader = new BufferedReader(new InputStreamReader(
                    new FileInputStream(inputFile), "UTF-16"));
            // UTF-8で一時出力ファイルに書き出す
            writer = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(tmpFile), "UTF-8"));

            // 122バイト毎読み込み、項目を解析する
            while ((charread = reader.read(tempchars)) != -1) {

                // 初期化
                if (temp.length() != 0) {
                    temp.delete(0, temp.length());
                }

                // 122バイトのデータを一レコードとして取得
                for (char chr : tempchars) {
                    temp.append(chr);
                }

                // 事業所情報の場合(レコード種別コード=0100)
                if (Const.RECORD_TYPE_JIGYOSYO.equals(temp.substring(28, 32))) {

                    // 事業所出力フラグをTRUEに設定
                    jigyosyo_flg = true;

                    // 事業所情報を退避する
                    jigyosyo_info = temp.toString();

                    // 被保険者情報の場合(レコード種別コード=0200)
                } else if (Const.RECORD_TYPE_HIHOKENSHA.equals(temp.substring(
                        28, 32))) {

                    // 取得処理日を取得
                    shoTokuSholiBi = temp.substring(32, 40);
                    // 離職日を取得
                    liShokuBi = temp.substring(61, 69);

                    // 入力レコード(離職日) ≧ 変数(処理日2年前) または
                    // 入力レコード(離職日) = "00000000" かつ
                    // 入力レコード(取得処理日) ≧ 変数(処理日1年前) の場合
                    if (compareDate(liShokuBi, shoTokuSholiBi)) {

                        // 事業所出力フラグがTRUEの場合、事業所情報を出力
                        if (jigyosyo_flg) {

                            writer.write(jigyosyo_info);
                            writer.write("\n");
                            jigyosyo_flg = false;
                        }

                        // 被保険者情報を出力
                        writer.write(temp.toString());
                        writer.write("\n");
                    }
                }
            }

        } catch (Exception e1) {
            throw e1;
        } finally {
            // 読み込みストリームを閉じる
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    throw e1;
                }
            }
            // 書き込みストリームを閉じる
            if (writer != null) {
                try {
                    writer.flush();
                    writer.close();
                } catch (IOException e1) {
                    throw e1;
                }
            }
        }

    }

    /**
     * 一時出力ファイルを削除する
     *
     * @throws Exception
     */
    public void delTmpFile() throws Exception{
        //一時出力ファイルが存在する場合、削除
        if (tmpFile.exists() && tmpFile.isFile()){
            tmpFile.delete();
        }
    }
   
    /**
     * 運用日付を設定する
     *
     * @param currentDate 運用日付
     */
    public void setCurrentDate(Calendar currentDate) {
        this.currentDate = currentDate;
    }
   
    /**
     * 適用統計台帳ファイルの変換を実行するメインメソッド
     *
     * @param args
     *     1.作業場所 <br>
     *     2.入力ファイル名 <br>
     *     3.運用日付
     */
    public static void main(String args[]) {
        //入力ファイル
        File file = null;
        //デートのフォーマット
        SimpleDateFormat parseTime = new SimpleDateFormat("yyyyMMdd");
        //運用日付
        Calendar unyou_date = Calendar.getInstance();
       
        // 引数は2個以下の場合、
        if (2 > args.length) {
            System.out.println("引数の個数を確認してください。");
            // 引数は3個の場合、
        } else {
            // 入力ファイルのフルパスを取得
            file = new File(args[0].trim() + File.separator + args[1].trim());
            // 入力ファイルが存在しない場合、
            if (!file.exists()) {
                System.out.println("作業場所か入力ファイル名が存在していません。");
                // 入力ファイルが存在する場合
            } else {
                IFHenKan henkan = new IFHenKan();
                //3番目のパラメータがあれば、運用日付として設定する
                if (3 == args.length){
                    if(args[2].length() != 8){
                        System.out.println("運用日付を正しく入力してください。");
                        System.exit(0);
                    }
                   
                    //入力パラメータを運用日付に変換
                    try {
                        Date date = parseTime.parse(args[2]);
                        unyou_date.setTime(date);
                       
                    } catch (ParseException e) {
                        System.out.println("運用日付を正しく入力してください。");
                        System.exit(0);
                    }
                }
               
                //運用日付を設定する
                henkan.setCurrentDate(unyou_date);
               
                System.out.println("実行中です、しばらくお待ちください。");
                log.info("雇用保険のインタフェースファイルの変換が開始しました。");
                // 入力ファイルの変換が成功する場合
                if (henkan.execute(file)) {
                    System.out.println("ファイルの変換が成功しました。\r\n" + "パス" + args[0]
                            + "の下にご確認ください。");
                    log.info("雇用保険のインタフェースファイルの変換が正常終了しました。");
                    // 入力ファイルの変換が失敗する場合
                } else {
                    System.out.println("ファイルの変換が失敗しました。\r\n"
                            + henkan.getClass().getResource("").getPath()
                            + File.separator + Const.LOG_NAME + "をご確認ください。");
                    log.info("雇用保険のインタフェースファイルの変換が異常終了しました。");
                }
            }
        }
    }
}

原创粉丝点击