写了一个小工具,遇到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("雇用保険のインタフェースファイルの変換が異常終了しました。");
}
}
}
}
}