javaによるcsv入力データのチェック処理について
- 評価
- クリップ 1
- VIEW 13K+
ABC123,テスト A,123456,060-5938-1234,
ABfD456,テスト E,123459,060-4697-8912,テスト
ABCD789,テスト F,1234581,060-4577-8913,
ABCD12,テスト G,1234582,060-1266-8914,
ABCD34,テスト H,12345834,060-7412-8915,
` のCSVファイルの入力データチェックプログラムを作成しています。 やりたい事はできたのですが、プログラムの書き方(無駄なところ) などをチェックしていただきたく思います。 ■CSVチェックプログラム項目 (1)A~D列以外(1~4列)に入力がある場合メッセージ表示 (2)入力値に誤りがある場合、メッセージ表示(1列目:英数字のみ/2列目:文字列型/3列目:数値型/4列目:電話番号) (3)各列(A~D列)に重複値がある場合は、メッセージ表示 (1)~(3)まですべてクリアしたら、「正常終了のメッセージ」を表示 下記に作成したプログラムを記述します。
package csvRead;
public class exec01 {
public static void main(String[] args) {
FieldChk01 Chk01 = new FieldChk01();
Chk01.method();
FieldChk02 Chk02 = new FieldChk02();
Chk02.method();
FReadDistinct Chk03 = new FReadDistinct();
Chk03.method();
if(Chk01.flg == 0 && FieldChk02.flg == 0 && Chk03.flg2 == 0) {
System.out.println("正常処理!!");
}
}
}
/**
* CSV入力値(A列~D列)のチェック
* @param args
*/
package csvRead;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.regex.Pattern;
public class FieldChk01 {
//処理フラグ(0なら正常:1以上なら異常)
int flg = 0;
public void method() {
try {
File file = new File("C:\\sample.csv");
// 入力ストリームを生成。( FileNotFoundException が発生 )
FileInputStream input = new FileInputStream(file);
/* 入力ストリームの読み込み。 ( UnsupportedEncodingException が発生 )
* ここでCSVファイルの文字コードを設定しないと文字化けします。 */
// 読み込むファイルの文字コード(SJIS)
InputStreamReader stream = new InputStreamReader(input, "SJIS");
// バッファに取り込み。
BufferedReader buffer = new BufferedReader(stream);
String line;
//読み込み件数
int count = 0;
//配列数
//int len1 = 0;
/* readLine()でバッファの1行を取り出す作業を、読み込める行が無くなるまでwhile文で実行。
* line = buffer.readline() でBufferedReaderが保持する一行を取出しているので、ループする毎に書き変わります。 */
while((line = buffer.readLine()) != null) {
// 取出した1行の文字セットを変換して新たに文字列を生成。
byte[] b = line.getBytes();
// String で UnsupportedEncodingException が発生 (変換したい文字コード:UTF-8)
line = new String(b, "UTF-8");
// 文字列をカンマ区切りで配列に分けて要素ごとに出力。
// line.splitの第2引数に"-1"を指定しないと、他の行と要素数が
// 違うため、出力結果の様にはなりません。
String[] columns = line.split(",", -1);
// 読み込み行数カウント
count++;
Pattern p1 = Pattern.compile("^[0-9]+$"); // 番号
Pattern p2 = Pattern.compile("^[0-9]{3}-[0-9]{4}-[0-9]{4}$"); // 電話番号
// 数字が含まれていればOK(社員番号)
Pattern p3 = Pattern.compile("^[0-9a-zA-Z]+$"); //社員番号(英数字文字のみが1文字以上)
Pattern p4 = Pattern.compile("^[^\\d]+$"); // 簡単な正規表現(単純に数字が含まれていないというのを確認)
for(int j = 0; j < columns.length; j++) {
//for(int j = 0; j < 4; j++) {
//System.out.println(j + " : " + columns[j]);
//データ型チェック開始
if(j == 0 && p3.matcher(columns[j]).matches() == false) {
System.out.println(count + "行目の" + "【1列目】入力規則違反。【入力値: " + columns[j] + "】");
flg++;
} else if(j == 1 && p4.matcher(columns[j]).matches() == false) {
//if(j == 1 && p4.matcher(columns[j]).matches() == false) {
System.out.println(count + "行目の" + "【2列目】入力規則違反。【入力値: " + columns[j] + "】");
flg++;
} else if(j == 2 && p1.matcher(columns[j]).matches() == false) {
System.out.println(count + "行目の" + "【3列目】入力規則違反。【入力値: " + columns[j] + "】");
flg++;
} else if(j == 3 && p2.matcher(columns[j]).matches() == false) {
System.out.println(count + "行目の" + "【4列目】入力規則違反。【入力値: " + columns[j] + "】");
flg++;
}
//データ型チェック終了
}
}
// 開いたストリームとバッファを閉じて関連するシステム・リソースを解放します。 ( IOException が発生 )
input.close();
stream.close();
buffer.close();
} catch (UnsupportedEncodingException | FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* CSV配列数のチェック(A~D列以外はNG)
* @param args
*/
package csvRead;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
public class FieldChk02 {
//処理フラグ(0なら正常:1以上なら異常)
static int flg = 0;
//public static void main(String[] args) {
// クラス化
public void method() {
// ここにコードを挿入
try {
// ファイルのパスを指定してオブジェクトを生成。
File file = new File("C:\\sample.csv");
//File file = new File(path2);
// 入力ストリームを生成。( FileNotFoundException が発生 )
FileInputStream input = new FileInputStream(file);
/* 入力ストリームの読み込み。 ( UnsupportedEncodingException が発生 )
* ここでCSVファイルの文字コードを設定しないと文字化けします。 */
// 読み込むファイルの文字コード(SJIS)
InputStreamReader stream = new InputStreamReader(input, "SJIS");
// バッファに取り込み。
BufferedReader buffer = new BufferedReader(stream);
String line;
//読み込み件数
int count = 0;
//配列数
int len1 = 0;
/* readLine()でバッファの1行を取り出す作業を、読み込める行が無くなるまでwhile文で実行。
* line = buffer.readline() でBufferedReaderが保持する一行を取出しているので、ループする毎に書き変わります。 */
while((line = buffer.readLine()) != null) {
// 取出した1行の文字セットを変換して新たに文字列を生成。
byte[] b = line.getBytes();
// String で UnsupportedEncodingException が発生 (変換したい文字コード:UTF-8)
line = new String(b, "UTF-8");
// 文字列をカンマ区切りで配列に分けて要素ごとに出力。
// line.splitの第2引数に"-1"を指定しないと、他の行と要素数が
// 違うため、出力結果の様にはなりません。
String[] columns = line.split(",", 0);
// 読み込み行数カウント
count++;
//配列数定義
len1 = columns.length;
for(int j = 0; j < columns.length; j++) {
//for(int j = 0; j < 4; j++) {
//System.out.println(j + " : " + columns[j]);
}
//配列数オーバーの行表示
if(len1 > 4) {
System.out.println(count + "行目の" + len1 + "列目に不正入力データがあります。");
flg++;
}
}
// 開いたストリームとバッファを閉じて関連するシステム・リソースを解放します。 ( IOException が発生 )
input.close();
stream.close();
buffer.close();
} catch (UnsupportedEncodingException | FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package csvRead;
/*
* ファイル読み込みクラス(文字コードSJIS)
*/
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class FRead {
public static String[] getLines(String fileName) {
List<String> list = new ArrayList<String>();
BufferedReader br = null;
try {
/* 入力ストリームの読み込み。
* ここでCSVファイルの文字コードを設定しないと文字化けします。
* 読み込むファイルの文字コード(SJIS) */
br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), "SJIS"));
String line = null;
// ファイル読み込み
while((line = br.readLine()) != null) {
list.add(line);
}
// br.close();
} catch (IOException err) {
// 開発中は、例外を握りつぶさないこと
err.printStackTrace();
} finally {
// リソースの終了処理は finally句で実施する
if(br != null) {
try {
br.close();
} catch (IOException ex) {
// Do nothing
}
}
}
return list.toArray(new String[0]);
}
}
``
package csvRead;
/*
* ファイル重複データ項目の表示
*/
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class FReadDistinct {
//処理フラグ(0なら正常:1以上なら異常)
int flg2 = 0;
//public static void main(String[] args) {
// クラス化
public void method() {
String[] csv = FRead.getLines("C:\\sample.csv");
List<String> result1 = new ArrayList<String>();
List<String> result2 = new ArrayList<String>();
List<String> result3 = new ArrayList<String>();
List<String> result4 = new ArrayList<String>();
for(String line : csv) {
result1.add(line.split(",")[0]);
result2.add(line.split(",")[1]);
result3.add(line.split(",")[2]);
result4.add(line.split(",")[3]);
}
// 重複チェックオブジェクト
Set<String> set1 = new HashSet<String>();
Set<String> set2 = new HashSet<String>();
Set<String> set3 = new HashSet<String>();
Set<String> set4 = new HashSet<String>();
//社員番号列(A列)
for(String user1 : result1) {
//setに追加してみて、追加できなかった(重複していた)場合
if(!set1.add(user1)) {
System.out.println("【社員番号】に重複データがあります。値:" + user1);
flg2++;
}
}
//社員名列(B列)
for(String user2 : result2) {
//setに追加してみて、追加できなかった(重複していた)場合
if(!set2.add(user2)) {
System.out.println("【社員名】に重複データがあります。値:" + user2);
flg2++;
}
}
//内線番号列(C列)
for(String user3 : result3) {
//setに追加してみて、追加できなかった(重複していた)場合
if(!set3.add(user3)) {
System.out.println("【番号】に重複データがあります。値:" + user3);
flg2++;
}
}
//電話番号列(D列)
for(String user4 : result4) {
//setに追加してみて、追加できなかった(重複していた)場合
if(!set4.add(user4)) {
System.out.println("【電話番号】に重複データがあります。値:" + user4);
flg2++;
}
}
}
}
宜しくお願いします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+2
FieldChk01クラス、FieldChk02クラス、FReadDistinctクラス、それぞれでファイルの読み込み処理を行っているのが無駄かと思います。
クラスは1つにして、コンストラクタでファイル名を渡す。
コンストラクタか読み込み用のメソッドを作って、ファイルを読み込む。
チェック用のメソッドを3つ用意する。
各メソッドでは読み込み済みのデータでチェック処理をして、結果を返却する。
このような感じの方がスマートな作り且つオブジェクト指向的な感じになると思います。
メインの方の流れだけ書いてみました。
メソッド名とかは適当です。
public class exec01 {
public static void main(String[] args) {
// FieldChkクラスをインスタンス化する
FieldChk Chk = new FieldChk("C:\\sample.csv");
// ファイル読み込み
Chk.read();
// チェック
if(Chk.check1() == 0 && Chk.check2() == 0 && Chk.check3() == 0) {
System.out.println("正常処理!!");
}
}
}
追記
methodというメソッド名は、処理の内容がわからないのでよろしくないです。
追記2
FieldChkクラスのサンプルです(イメージレベル)
class FieldChk {
private ファイル名;
private 読み込みバッファ;
コンストラクタ(引数){
引数をファイル名に保持
}
public read(){
FReadクラスでファイルを読み込んで、読み込みバッファに格納
}
public boolean check1(){
読み込みバッファを使ってチェック1処理
結果を返す(true/false)
}
public boolean check2(){
読み込みバッファを使ってチェック2処理
結果を返す(true/false)
}
public boolean check3(){
読み込みバッファを使ってチェック3処理
結果を返す(true/false)
}
}
check1~3メソッドの戻り値の型をbooleanに変更しました。
ok/ngを判定するメソッドなので、そちらの方がふさわしいかと思います。
なのでmainの方では、次のように判定できます。
if(Chk.check1() == true && Chk.check2() == true && Chk.check3() == true) {
もしくは
if(Chk.check1() && Chk.check2() && Chk.check3()) {
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
ttyp03さんがファイル読み込みについて言及しているので、それ以外で気になった点を挙げます。
1.シンプルなカンマ区切りの想定だけでいいのか
→この場合、項目内にカンマが入ってくるとそれさえも分割してしまいます。
2.エラーメッセージの形式が同じならをテンプレート、バインドの考え方を利用してみては?
→共通部分をあらかじめ用意し、可変部分を出力時に指定すれば、メッセージを変更したい時のメンテナンスも楽になります。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
ファイルの読み取り処理は一般的に時間がかかります。CSVの容量にもよりますが、一度全てを読み取った方が良いと思いますよ。
・先にCSVを読み取る処理を行い、CSVからStringの配列に変換する。
・各チェッククラスはStringの配列を受け取り、チェックを行う。
また、下記のようなことも考えられます。
・チェッククラスはもう少し具体的な名前にすべきかなとおもいます。
・チェックインターフェースを作成し、チェックメソッドを宣言する。各チェッククラスはチェックインターフェースを実装する。
・チェックメソッドの戻り値はboolean型にし、flgを廃止する。
・boolean型は==で比較しない。
チェックをするだけで具体的にどこが悪いかはわからないので、それがわかるようにするともっと実践的になると思います。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.33%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/06/20 09:05
(1)クラスは1つにする⇒FieldChk01/FieldChk02/FReadDistinctを1つのクラス「FieldChk」クラスにまとめる
→チェック用のメソッド(入力範囲のチェック(check1)/入力値のチェック(check2)/重複チェック(check3))
(2)FReadクラスでファイルを読む?(コンストラクタか読み込み用のメソッドを作るがあまり理解できません・・・)
(3)各メソッドでは読み込み済みのデータをチェック
のような処理の流れでしょうか?メイン処理に対しての「入力範囲のチェック(check1)」の書き方を教えて
いただけないでしょうか。
宜しくお願いします。
2016/06/20 09:28
(2)のところは、サンプルで書いたコードの Chk.read(); のところです。readメソッドの中でファイルを読み込むところを指しています。サンプルではコンストラクタでは読んでません。
読み込む処理は、FReadクラスを使っても良いですし、メソッド化しても良いかもですね。
> メイン処理に対しての「入力範囲のチェック(check1)」の書き方を教えて
サンプルコードの以下の所です。
if(Chk.check1() == 0 && Chk.check2() == 0 && Chk.check3() == 0) {
追記2でサンプルを追記しておきました。参考まで。
2016/06/20 10:43
````java
public class FieldChk {
// private ファイル名;
private String fname;
// private 読み込みバッファ??;
記述方法がわかりません。。
/* コンストラクタ(引数){
* 引数をファイル名に保持
* } */
public FieldChk(String fname) {
this.fname = fname;
}
public read(){
//FReadクラスでファイルを読み込んで、読み込みバッファに格納??
FReadクラス の中身の記述をすればいいでしょうか??
}
public boolean check1() {
// 読み込みバッファを使ってチェック1処理
読み込みバッファを使ってチェック1処理はどうすればいいでしょうか??
//結果を返す(true/false)
どのようにすればいいでしょうか??
}
}
````
また、ファイルは「FieldChkクラス」と「exec01クラス」の2つでいいのでしょうか?
宜しくお願いします。
2016/06/20 10:57
> 記述方法がわかりません。。
FReadDistinctクラスを流用すればいいかと。
String[] csv;
> //FReadクラスでファイルを読み込んで、読み込みバッファに格納??
> FReadクラス の中身の記述をすればいいでしょうか??
折角Freadクラスがあるので、今回はFReadクラスはそのまま残しましょうか。
なのでここもFReadDistinctクラスの流用してこんな感じでしょうか。
csv = FRead.getLines(fname);
> // 読み込みバッファを使ってチェック1処理
> 読み込みバッファを使ってチェック1処理はどうすればいいでしょうか??
methodメソッドでやっている処理を、読み込みバッファcsvに対して行うようにすればよいです。
> //結果を返す(true/false)
> どのようにすればいいでしょうか??
return flg == 0;
2016/06/20 11:18
>methodメソッドでやっている処理を、読み込みバッファcsvに対して行うようにすれば>よいです。
具体的にはどのようにするのでしょうか?
````java
public class FieldChk {
// private ファイル名;
private String fname;
// private 読み込みバッファ;
String[] csv;
/* コンストラクタ(引数){
* 引数をファイル名に保持
* } */
public FieldChk(String fname) {
this.fname = fname;
}
public void read() {
//FReadクラスでファイルを読み込んで、読み込みバッファに格納
//FReadクラス の中身の記述??
csv = FRead.getLines(fname);
}
public boolean check1() {
// 読み込みバッファを使ってチェック1処理
// 読み込みバッファを使ってチェック1処理
// 「FieldChk01」の処理
//結果を返す(true/false)
boolean flg = false;
return flg == true;
}
}
````
宜しくお願いします。
2016/06/20 11:26
要はファイル読み込みから、保持しているバッファを参照するように変えれば良いだけだと思います。
String line;
int count = 0;
for(count = 0; count < csv.length; count++ ){
line = csv[count];
~
あとflgのところは、既存クラスからの流用のイメージで書きましたので、型はintで良いです。
int flg = 0;
これをメソッド内のローカル変数に定義すればよいだけです。
2016/06/20 14:28
````java
import java.util.regex.Pattern;
public class FieldChk {
// private ファイル名;
private String fname;
// private 読み込みバッファ;
String[] csv;
/* コンストラクタ(引数){
* 引数をファイル名に保持
* } */
public FieldChk(String fname) {
this.fname = fname;
}
public void read() {
//FReadクラスでファイルを読み込んで、読み込みバッファに格納
csv = FRead.getLines(fname);
}
public boolean check1() {
// 読み込みバッファを使ってチェック1処理
// 読み込みバッファを使ってチェック1処理
// 「FieldChk01」の処理
String line;
int count = 0;
for(count = 0; count < csv.length;) {
line = csv[count];
String[] columns = line.split(",", -1);
count++;
Pattern p1 = Pattern.compile("^[0-9]+$"); // 内線番号
Pattern p2 = Pattern.compile("^[0-9]{3}-[0-9]{4}-[0-9]{4}$"); // 携帯番号
//Pattern p3 = Pattern.compile("^[a-zA-Z]{3}[0-9]{4}$"); //社員番号(英数字)
// 数字が含まれていればOK(社員番号)
Pattern p3 = Pattern.compile("^[0-9a-zA-Z]+$"); //社員番号(英数字文字のみが1文字以上)
Pattern p4 = Pattern.compile("^[^\\d]+$"); // 簡単な正規表現(単純に数字が含まれていないというのを確認)
// for(int j = 0; j < columns.length; j++) {
//for(int j = 0; j < 4; j++) {
//System.out.println(j + " : " + columns[j]);
//データ型チェック開始
if(count == 0 && p3.matcher(columns[count]).matches() == false) {
System.out.println(count + "行目の" + "【1列目】入力規則違反。【入力値: " + columns[count] + "】");
//flg++;
return false;
} else if(count == 1 && p4.matcher(columns[count]).matches() == false) {
//if(j == 1 && p4.matcher(columns[j]).matches() == false) {
System.out.println(count + "行目の" + "【2列目】入力規則違反。【入力値: " + columns[count] + "】");
//flg++;
return false;
} else if(count == 2 && p1.matcher(columns[count]).matches() == false) {
System.out.println(count + "行目の" + "【3列目】入力規則違反。【入力値: " + columns[count] + "】");
//flg++;
return false;
} else if(count == 3 && p2.matcher(columns[count]).matches() == false) {
System.out.println(count + "行目の" + "【4列目】入力規則違反。【入力値: " + columns[count] + "】");
//flg++;
return false;
}
// }
//結果を返す(true/false)
//int flg = 0;
return true;
}
return false;
}
}
````
作成してみたのですが、うまくいきません。。
もう少し教えていただけないでしょうか。
宜しくお願いします。
2016/06/20 14:37
あとは元々ループが二重だったのが一重になってますが大丈夫ですか?
こんな感じの構成になれば良いと思います。
読み込みバッファのループ {
カラムのループ {
チェック処理 → エラーで return false;
}
}
return true;
2016/06/20 15:02
まずは、count変数をexec01処理で呼び出してみようと思います。
読み込み行数が何行あるかのチェックです。
FieldChkクラスで、
````java
public int getCount() {
return getCount();
}
````
として、exec01クラスで「System.out.println(Chk.getCount());」として、読み込み件数を表示させたいのですが、されません。どこを修正したらいいでしょうか?
宜しくお願いします。
2016/06/20 15:09
return csv.length; では。
それとあまり細かいデバッグ的なことを依頼されるのは、そもそもの質問の趣旨とかけ離れてしまうのでご遠慮くださいませ。
他の方の回答も合わせて一旦自分なりに再構築して、その上で問題点を改めて質問した方が良いと思いますよ。
2016/06/20 15:49