テキストファイルの行単位の処理
各行から必要なデータを取り出す
データがテキストファイルの場合、1行に複数のデータが書かれていることが多い。例えば、図1の場合、1行に2個のデータが書かれている。
図1
これを、1行に1組のデータが書かれていると見て、読み込むことを考える。次のコードは、x[i][0]とx[i][1]に、i 番目の行のデータを読み込むものである。
for (int i = 0; i < n; i++){
String str = fin.readLine();
Extrct.CheckStr(str);
x[i][0] = Double.parseDouble(Extrct.getStrR(0));
x[i][1] = Double.parseDouble(Extrct.getStrR(1));
System.out.printf("%10.0f %10.0f\n", x[i][0], x[i][1]);
}
データの開始行と終わりの行は、図1のデータでは、スラッシュ / で始まる行によって示されている。
データの取り出しは、ExtractSubString型のオブジェクトExtrctによって行っている。
ExtractSubString Extrct = new ExtractSubString();
char ck_c[] = {' ', '\t', ',', ' ', '、'};
Extrct.ck_list = ck_c;
データの区切り文字を Extrct.ck_listに設定している。それらの区切り文字によって区切られた p 番目の空ではない文字列をExtrct.getStrR( p ) によって取り出している。
クラス型ExtractSubStringを宣言しているファイルExtractSubString.java の内容は以下のようである。
package ExtractSubString;
//
// Y.
Okamoto, 2015.12
//
public class ExtractSubString {
public
char ck_list[]; // 文字列のセパレータ文字用配列
public
int n_match; // 文字列に含まれるセパレータ文字の総数
public
String wd_list[]; // セパレータ文字で区切られた文字列(セパレータが連続すれば空の文字列"")の配列
public
String wd_listR[]; // セパレータ文字で区切られた文字列で空の文字列でないものの配列
public
int nR;
//
// 引数の文字が、セパレータ文字列と一致すれば true を返す。
//
boolean
checkChar(char c){
boolean
v = false;
for
(int i = 0; i < ck_list.length; i++)
if
(c == ck_list[i]) v = true;
return
v;
}
//
// 引数の文字列 str に含まれるセパレータ文字の個数を n_match に設定
// セパレータ文字で区切られた文字列を配列 wd_list に設定
// セパレータ文字で区切られた文字列で空文字列でないものを配列
wd_listR に設定
//
public
void CheckStr(String str){
//
// セパレータ文字を数えて n_match に設定
//
n_match
= 0;
if
(str.length() > 0){
for
(int i = 0; i < str.length(); i++)
for
(int j = 0; j < ck_list.length; j++)
if
(str.charAt(i) == ck_list[j])
n_match++;
}
//
// セパレータ文字で区切られた文字列を配列 wd_list に設定
//
wd_list
= new String[n_match + 1];
if
(str.length() < 1){
wd_list[0]
= "";
}else
if(n_match < 1){
wd_list[0]
= str;
}else{
int
s_pos = 0, e_pos = 0;
for
(int k = 0; k < n_match; k++){
while(!checkChar(str.charAt(e_pos)))
e_pos++;
if
(s_pos == e_pos){
wd_list[k]
= "";
}else{
wd_list[k]
= str.substring(s_pos, e_pos);
}
if(k
< n_match - 1)
s_pos
= ++e_pos;
}
if(e_pos
== (str.length() - 1)){
wd_list[n_match]
= "";
}else{
wd_list[n_match]
= str.substring(e_pos + 1);
}
}
//
// セパレータ文字で区切られた文字列で空でない文字列を配列
wd_;listR に設定
// 空でない文字列の個数を nR に設定
//
nR
= 0;
wd_listR
= new String[n_match + 1];
for
(int i = 0; i < n_match + 1; i++){
if
(!(wd_list[i].equals(""))){
wd_listR[nR]
= wd_list[i];
nR++;
}
}
}
//
// セパレータ文字で区切られた文字列の p 番目の文字列を返す
// p
番目の文字列が配列 wd_list に格納されている文字列数より大きいときは、空の文字列 "" を返す。
//
public
String getStr(int p){
String
str;
if
(p > n_match + 1){
str
= "";
}else{
str
= wd_list[p];
}
return
str;
}
//
// 空でない文字列の p 番目の文字列を返す。
// 空でない文字列の個数より大きい値が p に設定されていたときは、空の文字列 "" を返す。
//
public
String getStrR(int p){
String
str;
if
(nR < 1){
str
= "";
}else
if(p < nR){
str
= wd_listR[p];
}else{
str
= "";
}
return
str;
}
}
上のクラス型のオブジェクトにより、1行から2つずつのデータを取り出し、およびのそれぞれの和を求めるプログラムReadData.javaを以下のように用意した。
package ReadData;
import java.io.*;
import ExtractSubString.*;
public class ReadData {
public
static void main(String args[]){
try{
//
// キーボードからの入力用ストリーム cin を設定
//
BufferedReader
cin = new BufferedReader(new InputStreamReader(System.in));
//
// ファイルからの読み込み用ストリーム fin の設定
//
System.out.print("Input
Data File = ");
String
Inflnm = cin.readLine();
BufferedReader
fin = new BufferedReader(new FileReader(Inflnm));
//
// ファイルへの出力用ストリーム fout の設定
//
System.out.print("Output
File Name = ");
String
Outflnm = cin.readLine();
PrintWriter
fout = new PrintWriter( new BufferedWriter( new FileWriter(Outflnm)));
//
// 先頭がスラッシュ / で始まる最初の行を探す。
//
do{
String
str = fin.readLine();
if
(str.length() > 0)
if
(str.charAt(0) == '/')
break;
}while(true);
//
// 先頭がスラッシュ / で始まる次の行までの行数を数える
// この行数 n が、データの書かれている行数である。
//
int
n = 0;
do{
String
str = fin.readLine();
if
(str.length() > 0)
if
(str.charAt(0) == '/')
break;
n++;
}while(true);
fin.close(); // 入力ファイルを閉じる。
//
// データ入力用配列 x を用意。
//
double
x[][] = new double[n][2];
//
// 入力データファイルを再度開く
//
fin
= new BufferedReader(new FileReader(Inflnm));
//
// 行の先頭がスラッシュ / で始まる最初のまで読み進む。
//
do{
String
str = fin.readLine();
if
(str.length() > 0)
if
(str.charAt(0) == '/')
break;
}while(true);
//
// 入力文字列をセパレータ文字で区切るためのオブジェクト
Extrct を準備する。
//
ExtractSubString
Extrct = new ExtractSubString();
//
// セパレータ用文字を配列 ck_c として用意。
//
char
ck_c[] = {' ', '\t', ',', ' ', '、'};
//
// 用意したセパレータ文字の配列を、Extrct のセパレータ文字の配列 ck_list に設定。
//
Extrct.ck_list
= ck_c;
//
// データを1行ずつ読み込み処理する。
//
for
(int i = 0; i < n; i++){
String
str = fin.readLine(); // データを1行読み込む。
Extrct.CheckStr(str); // 読み込んだデータ文字列を Extrct に設定して、セパレータで区切られた文字列を用意する
x[i][0]
= Double.parseDouble(Extrct.getStrR(0)); // 空でない 0 番目の文字列を取り出して、数値化して配列の要素に設定
x[i][1]
= Double.parseDouble(Extrct.getStrR(1)); // 空でない 1 番目の文字列を取り出して、数値化して配列の要素に設定
System.out.printf("%10.0f
%10.0f\n", x[i][0], x[i][1]); // 配列に設定した値の表示
}
fin.close(); // データを n 行読み込んだので、入力ファイルを閉じる。
//
// 出力ファイルへの書き出し
//
fout.println("Input
data file = " + Inflnm);
fout.println();
fout.println("Data
= ");
double
sum1 = 0.0, sum2 = 0.0;
for
(int i = 0; i < n; i++){
fout.printf("%10.0f
%10.0f\n", x[i][0], x[i][1]);
sum1
+= x[i][0];
sum2
+= x[i][1];
}
fout.printf("Sum
=\n%10.0f %10.0f", sum1, sum2);
System.out.printf("Sum\n%10.0f
%10.0f", sum1, sum2);
fout.close(); // 出力ファイルを閉じる
System.out.println("\n"
+ Outflnm + " was saved.");
}
catch(IOException
e){
System.out.println("Exception...");
System.out.println(e.toString());
}
}
}
図1のデータに対して実行すると、図2の出力ファイルを得る。
図2
プログラムの実行後のコンソールは図3に示すようになっている。
図3
プログラムの実行時のカレントディレクトリはプロジェクトのフォルダの直下、src ディレクトリと同じフォルダである(図4)。
図4
フォルダ src 内に、パッケージのフォルダ(ディレクトリ)が作成されている(図6)。
図5
上のプログラムにおけるパッケージ(*.javaファイル)は、圧縮ファイルsrc_ReadTextLine.zipにまとめた。圧縮src_ReadTextLine.zipのパッケージファイルのEclipseにおける利用法については、別のウェブサイトで説明している。
なお、心理学データ分析の解説書<岡本安晴「心理学データ分析と測定: データの見方と心の測り方」勁草書房>を用意しているが、参考になれば幸いである。