page5(update:2017/10/31)


[ prev | next | index ]

5.制御構造

 javaの実行制御の構文はC言語に似ていますが、条件判断はboolean型の値で行われます。さらに、breakやcontinueでは対応する繰り返し構文をラベル を用いて指定できます。

※java5以降では拡張for文が使えます。しかし、この紹介は後のCollectionFrameworkで行います。

C言語に無い新しい構文として、java言語には例外処理が有ります。今回はこの例外処理をぜひ理解してください。

目次

[目次]

5.1 処理の分岐

普通は書かれた順番に命令を実行するので、そのプログラムの処理の流れは1本になっています。しかし、条件に応じて処理を分けたい場合もあります。そこで、処理の流れを分岐させる為の構文としてif 文が用意されています。

5.1.1 if文 

if(条件式) 文A ;else 文B

条件式の値はboolean型でなければいけません。

※boolean型の値をプログラム中に直接記述するリテラルにはtruefalseを使います。

動作: 条件式の値がtrueなら文Aを実行、falseなら文Bを実行します。文Bが無い場合はelseの部分を省略して書くことができます。

if(条件式) 文A 

文Aや文Bの部分に複数の文を入れたいなら, 複文(compound statement/block/ブロック)を使います。

if(条件式) {文A1;文A2;文A3;文A4;} else {文B1;文B2;}

{ }で囲まれた部分が複文です。

論理演算子と関係演算子、等価演算子

条件式を書く為に論理演算子関係演算子等価演算子が用意されています。これは暗記して下さい

C言語と外見上同じに見えますが戻り値がboolean型である点が異なります。

論理演算子 一般形 意味
&& e1 && e2 論理AND:e1、e2がともにtrueなら戻り値true、
異なる場合にfalse
(注)e1がfalseならe2を評価せずにfalseを戻す
|| e1 || e2 論理OR:e1、e2が何れかtrueなら戻り値true、
異なる場合にfalse
(注)e1がtrueならe2を評価せずにtrueを戻す
! ! e 論理NOT: eがtrueなら戻り値false、 eがfalseなら戻り値true
関係演算子
   
< e1 < e2 e1<e2なら戻り値true、異なる場合にfalse
<= e1 <= e2 e1≦e2なら戻り値true、異なる場合にfalse
> e1 > e2 e1>e2なら戻り値true、異なる場合にfalse
>= e1 >= e2 e1≧e2なら戻り値true、異なる場合にfalse
等価演算子    
== e1 == e2 等号:  値が等しいなら戻り値true、異なる場合にfalse
!= e1 != e2 不等号: 値が等しいなら戻り値false、異なる場合にtrue

等価演算子 == の注意点

等価演算子は変数の値が等しいときにtrueとなります。基本データ型ならばこのことで混乱は生じませんが、参照データ型の場合は注意が必要です。でも参照変数の場合は注意が必要です。

1) 参照変数の値が等しい場合

参照変数p,qでp==q が真であれば,p,qは同一のインスタンスを参照している。 従ってp,qから参照したインスタンスの中身は常に同じです。

2) 参照変数の値が等しくない場合でもインスタンスの中身は等しいことがある

 参照変数p,qでp==q が偽であれば,p,qは異なるインスタンスを参照しています。しかし,p,qから参照したインスタンスの中身が等しいことも有りえます。

3) java言語ではインスタンスの中身が同じか否かを返すメソッドequalsが用意されています。

equals は既定値では同じインスタンスを参照する場合にtrueを返します。

しかし,文字列Stringのequalsは文字列の中身が等しい場合にtrueを戻すように上書きされています。

※複雑なインスタンスになると中身が同じと判定する基準も定義が難しくなりそうです。例えば2つのウインドウに同じ文字列が表示されていれば同じなのか? 文字の大きさが違えば違うのか? といったことが考えられます。そこで,クラスごとにequalsメソッドを上書きして動作を変更することが可能です。

public class TestEq
{
    public static void main(String args[])
    {
        int a=12,b=12;
        System.out.println("a=12,b=12;");
        System.out.println("a==b :" + (a==b) );
        System.out.println();
        //
        String c="ABC123",d="ABC",e="123",f=d+e;
        System.out.println("String c=\"ABC123\",d=\"ABC\",e=\"123\",f=d+e;");
        System.out.println("c=\""+c+"\"");
        System.out.println("f=\""+f+"\"");
        System.out.println("c==f :" + (c==f) );
        System.out.println("c.equals(f) :" + (c.equals(f)) );
    }
}
/*実行結果
a=12,b=12;
a==b :true

String c="ABC123",d="ABC",e="123",f=d+e;
c="ABC123"
f="ABC123"
c==f :false
c.equals(f) :true
*

5.1.2 多重分岐

入れ子のif文で多重分岐を記述できます。3個に分岐する場合は次の様に書きます。

if(条件式1文A; else if(条件式2文B; else 文C

これはif文のelse以下がさらにif文になった形です。上記の場合は条件に応じて文A〜Cの何れか1つが必ず実行されます。

ソースプログラムはフリーフォーマットなので、改行や空白はコンパイルにおいては区切りとしての意味しか持ちません。プログラマはプログラム構造が解かり易い様に自由に改行や空白を入れることができます。上記の if 文も下記の様に改行を入れて記述することが多いでしょう。

if(条件式1)   文A;
else if(条件式2文B;
else        文C

上の文Cをさらにif文にすればいくらでも分岐の数を増やせます。

5.1.3 switch文

整数型文字型の式の値で多重分岐する場合によく使われる形です。下記に構文の形を示します。式の値と等しいcase文に処理が跳び,その後は下へ順番に実行しbreak文にぶつかるとswitch文から抜けます。C言語のswitch文と同様です。

switch(){
        case 定数式1://式の値が定数式1に等しければここから始める
                ....
                break;//ここでswitch構文の外へ
        case 定数式2:
        case 定数式3:
                //この様に重ねると式の値が定数式2or3でここに来る
                ....
                break;//ここでswitch構文の外へ
        default://該当するcase文がない場合はここから始める(不要なら省略できる)
                ....
}

java7の新機能

式や跳び先の定数式にjava7からは文字列:Stringが使えます。

switch(文字列を与える式){
        case "文字列1":
                ....
                break;
        case "文字列2":
                ....
                break;
        case "文字列3":
                ....
                break;
        default:
                ....
}

[目次]

5.2 繰り返しの構文

 構文はC/C++言語とほとんど同じです。

5.2.1 while文

もっとも一般的な繰り返しの構文はwhile文で

while(条件式) 文 

動作:
条件式の値がtrueなら文を実行し再び条件式の判定を行う。結果的に条件式の値がfalseになるまで文を繰り返し実行する。

(注)文を実行することで条件式の値が変化しない場合は無限ループになる可能性がある。

下記の様に文を複文にすることでまとまったプログラムを繰り返し実行できる。

while(条件式){文1; 文2;... }

5.2.2 for文

繰り返しの回数があらかじめ決まっている場合にはfor文をよく使います 。

※forとwhileの使い分けに決りがあるわけではない。whileで書ける繰り返しはforでも書けるし、逆も同様です。

for(初期設定式;条件式;再設定式) 文 

動作:
初期設定式を実行後、条件式の値がtrueなら文を実行する。再設定式を実行後、再び条件式の判定を行う。文を複文にすることでまとまったプログラムを繰り返し実行できる。

多くの場合次の様な形で使われる。

.....
int sum=0;
for(int n=0; n<100 ; n++ ){/*繰り返しの初期設定条件再設定がまとまっている*/
        sum=sum+1.0/n;
}

(注)java言語ではfor文の繰り返しの回数を数える変数はfor文の初期設定式で宣言することもよく行われます。
for(
int n=0 ; n<10 ; n++){....

 ※初期設定式と再設定式のところはカンマで区切って複数の式を書くことができます。 これはC言語のカンマ演算子ではなくjavaのfor文の書式です。
for(i=0,k=10 ; i<k ; i++,k--){...

5.2.3 do while文

繰り返しの文を実行後条件判定をする場合の構文です。

do 文; while(条件式);

あるいは

do {文1;文2;...} while(条件式);

while文やfor文は繰り返しの文を実行する前に条件判定を行うのに対してこの構文は繰り返しの文の後で条件を判定します。

5.2.4 break文

繰り返しの構文から脱出する命令で基本的にはC言語と同じですが、ラベルを使うことで入れ子の繰り返し構文から一度に抜け出すことも可能です。

.....
LOOP://ラベルの宣言、直後のfor文を指す名前LOOPを宣言している
for(int i=0;i<10;i++){
    for(int j=0;j<10;j++){
        ....
        if(error)break LOOP;//ラベルで示された繰り返し構文から脱出し文Aに移動
        ....
    }
    ....
}
文A;
...
 

5.2.5 continue文

これはループからの脱出ではなくて、whileやdo while文なら次の処理を条件式へ移す命令です。for文の場合は再設定式へ処理を移します。こちらもbreak文と同様にラベル名を使うことが可能です。

[目次]

5.3 例外処理

プログラムの実行中に例外的な状況になってプログラムの実行順番を変えたい場合が在ります。しかし、例外が起きる可能性のあるところに、いちいち条件分岐のプログラムを書くと、プログラムの流れがとても解りにくくなります。さらに、例外に対応するには関数呼び出しを遡って処理する必要が出てきたりしますから大変です。そこで、java言語には例外処理の手順が構文として用意されています。

※めったに使われることの無い、プログラムの本流ではない処理が頻繁にプログラムの中に出てくると、本来の流れが把握できなくなる。 C言語のプログラムであれば、例外が起きるところでは例外の有無をチェックして処理部へgoto文で跳ぶ形のプログラムを書くことになるでしょう。しかしgotoでは自由度が在りすぎて例外処理のパターンが定まりません。

java言語は指定された範囲で例外が起きたら決められた場所に飛ぶ例外処理の構文を用意しました。tryとcatchの構文です。

例外処理の構文

例外処理の構文は3種類のブロックでできて居ます。通常処理を記述するtryのブロック,例外が起きた場合の処理を記述するcatchのブロック,例外が起きても起きなくても最後に必ず実行したい後始末を記述するfinallyのブロックの3種類です。

catchのブロックは例外の種類に応じて複数記述することができますし,何も処理しない場合は省くこともできます。後始末をが不要ならfinallyのブロックも省くことができます。しかし,catchとfinallyのブロックを両方とも省くのは構文エラーです。

処理の流れ

下の模式図のように,まずtryのブロックが実行されます。ここで例外が起きなけれなcatchブロックは実行されず,finallyのブロックが在ればはこれを実行して例外処理の構文から出ます。 これが通常の処理の流れです。

tryのブロック内で例外が起きたら,例外の情報を記録した例外クラスのインスタンスを生成し,直ちにtryのブロックを抜けます。この流れを「例外を投げる」と表現します。

投げられたインスタンスに対応するcatchのブロックがあれば,これを例外処理として実行します。最後に,finallyのブロックが在ればはこれを実行して例外処理の構文から出ます。

※catchのブロックでreturnしているような場合でも,finallyのブロックはreturn前に実行せれます。

処理できる例外の種類はcatchブロックの参照引数のクラス型で示します。複数のcatchのブロックの先頭から順番に発生した例外に対応するクラス型を探して,最初に見つけたcatchのブロックを例外処理として実行します。catchのブロックに処理が移ることで,投げられた例外は受け取られたことになります

しかし,対応するcatchのブロックが無かった場合やcatchのブロックが省かれている場合は,例外は受け取られません。例外処理の構文の外へ例外をそのまま投げることになります。この場合でも,finallyのブロックが在れば,構文を出る前にfinallyのブロックを実行します。

 

例外インスタンス

例外の情報を記録するオブジェクトについて,大事なことなので,まとめておきます。

1) 例外の起きる場所は1か所とは限りません。そこで例外が起きたら例外インスタンスを生成し例外の情報を記録してcatchのブロックに飛ぶ仕組みになっています。catchの後の( )で囲われた部分はこの例外インスタンスの参照変数を書きます。関数の引数と同じような表現になっています。例外処理に必要な情報はこの例外インスタンスから得ることができます。

2) 例外の種類も一つとは限りません。例外の種類は例外インスタンスのクラスで区別します。catchの後の( )内に書かれたクラス型と例外インスタンスの型が一致する最初のcatchブロックが例外に対応する処理として実行されます。 catchは例外の型に応じて複数書くことができます。

5.3.1 例外を投げる

java言語では例外処理に抜け出すために例外(Exception)投げ(throw)ます。 

例外を投げるには、まず例外インスタンスを作らなければいけません。例外インスタンスはExceptionクラス、あるいはExceptionを継承したクラスのインスタンスです。次に、このインスタンスをthrowで投げると例外が発生する仕組みになっています。

....
if(例外が発生したか?){
    Excetption ex;
    ex=new Exception("何かメッセージがあればここに");
    throw ex;
}
....
//もう少し簡潔に書くことにすれば
if(例外が発生したか?)throw new Exception("何かのメッセージ");
....

投げた例外をメソッド(関数)の中で処理せずに、メソッドを呼び出した側に投げ上げることが可能です。

この場合はメソッドの定義で、例外を投げることを明記する必要があります。例外が発生するとメソッドの途中から脱出して呼び出し側のプログラムに例外を投げます。doSomthingメソッドがException型の例外を投げるかもしれないときは次のようにメソッドの頭部でthrowsで投げる例外を明記します。

※数種類の例外を投げる可能性がある関数ではthrowsの後にカンマ区切りで複数の例外のクラスを記述します。

static public double doSomthing(double x) throws Exception
{
    if(x<0)throw new Exception("負の数の平方根は計算できない");//メッセージ付きのExceptionを投げる
    return Math.sqrt(x);
}

もし上の関数doSomthingを次の関数function1が呼んでいる場合、function1は渡された例外を自分で処理するか、さらにfunction1を呼んでいる関数に投げるかのいずれかの処理を行 う必要があります。自分で処理しない場合は次のプログラムの様になります。この場合はdoSomthingが投げた例外をそのままfunction1を 呼んでいる関数に投げます。

static public void function1() throws Exception
{
    for(double x=10;-2<x;x--)
    {  
        double s=doSomthing(x);//doSomthing()で例外発生の可能性あり
        System.out.printf("%f %f%n",x,s);
    }
}

もしmain関数でも次の様に例外を投げてしまえば、プログラムはそこで終了することになります。

static public void main(String args[])throws Exception
{
    function1();
}

★例外を投げる可能性のある関数を呼ぶ場合、投げられた例外を受け取るか、さらに上に例外を渡すかです。どちらかの処理をしないとコンパイルエラーになってしまいます。

例外は関数呼び出しの順番をさかのぼることができます。

public class ThrowsException
{
    static public void main(String args[])throws Exception
    {
        function1();
    }
    //例外を投げる関数は必ず投げる例外の種類を宣言する
    static public void function1() throws Exception
    {
        for(double x=10;-2<x;x--){
            double s=doSomthing(x);//doSomthing()で例外発生の可能性あり
            System.out.printf("%f %f%n",x,s);
        }
    }
    //
    static public double doSomthing(double x) throws Exception
    {
        if(x<0)throw new Exception("負の数の平方根は計算できない");//メッセージ付きのExceptionを投げる
        return Math.sqrt(x);
    }
}
/* 実行結果
10.000000 3.162278
9.000000 3.000000
8.000000 2.828427
7.000000 2.645751
6.000000 2.449490
5.000000 2.236068
4.000000 2.000000
3.000000 1.732051
2.000000 1.414214
1.000000 1.000000
0.000000 0.000000
Exception in thread "main" java.lang.Exception: 負の数の平方根は計算できない
at ThrowsException.doSomthing(ThrowsException.java:18)
at ThrowsException.function1(ThrowsException.java:11)
at ThrowsException.main(ThrowsException.java:5)
*/

finally

例外を関数の外へ投げる場合でも,関数から抜け出すときに必ず実行してほしい処理がある場合に使います。

finallyのブロックだけを書くことはできないので,tryのブロックと合わせて記述することになりますが,例外を投げて,関数から抜ける場合でも,必ずfinallyのブロックが実行されます。

try{


}finally{
   tryから抜けるときに実行したい処理のプログラム
   多くの場合tryのブロックで確保したリソースの開放などを記述する
} 

5.3.2 例外を受け取る

例外を受け取る場合、javaではtry catchの構文を使って捕捉します。 次のページで説明する入出力に用いるライブラリには例外を発生させる関数が多く、この様な関数を使う場合は必ず例外を処理する必要が有ります

try{
        ...
        //ここで例外:Exceptionが発生するとcatch(Exception e)へ処理が跳ぶ
        ...

}catch(Exception e){ //例外がExceptionならここで処理
        e.printStackTrace();//とりあえずこう書いておく
}

例外インスタンスのメソッドprintStackTrace()は簡単な出力フォーマットで、
例外の型、例外メッセージ、例外の発生までの 関数呼び出し経路」を書き出します。

※関数呼び出しではスタックに戻り先を積み上げます。StackTraceとは,このスタックにある情報をもとに関数呼び出しを遡ってトレースするという意味です。

投げられた例外クラス名:例外のメッセージ文字列
	at 例外が発生したメソッド名(ソースファイル名:行番号)
	at 上を呼び出したメソッド名(ソースファイル名:行番号)
	---- 以下同様に例外を受け取ったメソッドまで遡る ----

とりあえずこれで例外が発生するまでのメソッド呼び出しの流れを知ることができます。

例題5

例外を投げるメソッドtest、test2を作りmainから呼び出して発生した例外をmainメソッド内で受けとる。受けとった例外のprintStackTrace()メソッドを呼 んで、例外の種類 と、例外が投げられるまでの関数呼び出しの経路を書き出します。実行結果で例外の発生場所等を確認してください。

/*
ExceptionTest.java 
以下は説明のために行番号を付けていますのでコンパイルできません
*/

01:public class ExceptionTest{
02:        static public void main(String arg[]){
03:                int data[]={-1,0,1};
04:                for(int i=0;i<data.length;i++){
05:                        try{
06:                                test(data[i]);//例外を発生する関数を呼ぶ        
07:                                
08:                        //例外やエラーを分けて捕まえる事が可能  
09:                        }catch(Exception e){
10:                                System.err.println("例外処理");
11:                                e.printStackTrace();
12:                        }catch(Error e){
13:                                System.err.println("エラー処理");
14:                                e.printStackTrace();
15:                                //エラーを捕まえるとも可能
16:                        }
17:                }
18:                System.err.println("\n mainを終了します \n");
19:        }
20:        
21:        static void test(int data) throws Exception//投げる例外を明記
22:        {
23:                if(0<=data)test2(data);
24:                else throw new Exception("DATAが負");
25:        }
26:        
27:        static void test2(int data) throws Exception//Errorはthrowsに書く必要がない
28:        {
29:                if(data==0)throw new Exception("DATAが0");
30:                throw new Error("DATAが正");//例外の他にエラーも投げることが可能
31:        }
32:        
33:}
実行結果を見ると発生した例外、発生した場所、発生したときの関数の呼び出し状況が表示されます。
以下が実行結果
例外処理
java.lang.Exception: DATAが負
        at ExceptionTest.test(ExceptionTest.java:24) 
        at ExceptionTest.main(ExceptionTest.java:6)
これはmain関数内のプログラムの6行目からtestが呼ばれ、そのプログラムの24行目で
 Exception("DATAが負") が投げられたことを示している。

例外処理
java.lang.Exception: DATAが0
        at ExceptionTest.test2(ExceptionTest.java:29) ※main>test>test2と呼んだところで発生
        at ExceptionTest.test(ExceptionTest.java:23)
        at ExceptionTest.main(ExceptionTest.java:6)
エラー処理
java.lang.Error: DATAが正
        at ExceptionTest.test2(ExceptionTest.java:30) main>test>test2と呼んだところで発生
        at ExceptionTest.test(ExceptionTest.java:23)
        at ExceptionTest.main(ExceptionTest.java:6)

 mainを終了します ※エラーを捕まえて処理したので,プログラムは正常終了します

★java言語では実行時エラーが検出されるとErrorクラスのインスタンスを作成して例外と同じように投げます。エラーは例外と違ってtry-catchの構文で処理する義務が有りませんが、try-catch構文で捕まえてエラーの処理を行う事も可能です。

finally

try-catchの構文から抜けるときに必ず実行してほしい処理をfinallyに記述できます。tryブロックでファイルを開くような処理を行っていると,後始末でファイルを閉じる必要があります。tryブロックにファイルを閉じる処理まで書いても,ここで例外が発生すると例外処理に飛ぶので,まだファイルを閉じていない場合は,catchブロックでもファイルを閉じる処理を書く必要があります。このままでは,重複してファイルを閉じる処理を書くことになります。

finallyは例外処理の構文から抜けるときに後始末として必要な処理を書くためのものです。finallyのブロックにファイルを閉じるプログラムを書けば,tryブロックやcatchブロックに重複したプログラムを書く必要が無くなります。 

try{


}catch(Exception ex){

}finally{
   try-catchから抜けるときに実行したい処理のプログラム
} 

5.3.3 assertion(表明) −−jdk1.4以降の機能です−−

Error(エラー)もthrowで投げる事が可能です。これを利用してプログラム中に実行時条件チェック(assertion:表明)を書き込めます。例えば

if(条件==false)throw new Error("XXXで条件が満たされませんでした...");

をプログラムに挿入してテスト実行すれば、条件が満たされないとプログラムが止まりエラーメッセージで詳しい状況を知ることができます。Errorはcatchする義務が無く、メソッドの定義でthrowsを書く必要もないので,プログラムにErrorを投げる文を挟むことは容易です。

しかし、この書き方では条件チェックをやめるにはソースを元に戻すしかありません。 テスト用と出荷用で2つのソースファイルを管理することにもなってプログラムの更新等で問題が起きるでしょう。

jdk1.4から新しく表明と呼ばれる機能が加えられました。これはAssertionErrorを投げる機能です。実行時の条件チェックに使いますが、エラーなのでいちいちcatchやthrows等の記述を必要としません。また実行時にこの機能をON/OFFでき、既定値はOFFです。

jdk1.4からサポートされる。実行時の条件チェック(assertion)
書式1: assert  ;
書式2: assert  : 式2 ; 

falseのときAssertionErrorを投げる
ここで式2はAssertionErrorの生成に使われる文字列等の値。

コンパイルでは

javac -source 1.4 XXX.java

-source 1.4のオプションが必要で、実行では

java -ea ClassName

 -eaのオプションが必要です。オプション無しか-daなら動作チェックは無効 となる。
コンパイル済みの運用クラスファイルが起動オプションでテストモードで実行できます。

[目次]

5.4 課題4(EditerFrame.javaText.java、初期ファイルP4.java

ファイル入出力を実装したテキストエディターのサンプルです。このプログラムの一部分を写経して完成させてください。

課題2を書き直し、ファイルとの入出力部分を別クラスに切り離したのものです。ソースファイルも3つに分けています。初期ファイルP4.javaEditerFrame.javaText.javaを同じフォルダ内に置いてP4.javaを編集で開いて写経しコンパイルすれば実行可能です。

※ファイルを置くまでのフォルダ名に日本語など倍角の文字を使うのは避けてください。JReport.jarから見つけられないことが在ります。

今回の例外処理構文が使われています。写経するのはgetTextsetTextメソッドのみです。コメントは省いてください。ちょっと長めです。※コメントは間違いのもとになるので写経から省くこと

※字下げは重要なので行の書き出し位置は手本のように必ず下げること。tryとcatchの対応が取れているか確認してください。 IEで見ると手本の字下げが正しく表示されないことがあります。Firefoxで見てください。

レポート送信ではファイルの読み書きを行って、正常に動作することを確認すること(完成品P4.jar
※読み書きテストにP4.javaを使わないでください、書き換えてしまうと不正なファイルになり、元に戻せません。

/*P4.java*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
public class P4
{
    public static void main(String args[]){
        JFrame window=new EditorFrame();
        window.setSize(600,300); 
        window.setVisible(true); 
    }
}
//ファイルを相手にテキストの読み書きができるTextインターフェースを実装したクラス
//入出力関係のライブラリーでは例外を投げるメソッドが多いため try catch の構文が多くなる
class TextFile implements Text
{
    //
    // クラスのメンバーを利用してファイル選択機能を実装
    //
    static private JFileChooser chooser = new JFileChooser();
    static private File chooseFile(boolean createFile)
    {
        File sfile=null;
        synchronized(chooser){//一つのものを使いまわすので同時に2つは開かないように制限
            int ret;
            if(createFile)ret=chooser.showSaveDialog(null);
            else ret=chooser.showOpenDialog(null);
            if(ret!= JFileChooser.APPROVE_OPTION) return null;

            sfile=chooser.getSelectedFile();
            if(sfile==null || sfile.isDirectory())return null;
            //ファイルがまだ作られていないときは新規に作成
            if( ! sfile.exists()){
                try{
                    sfile.createNewFile();
                }catch(IOException e){
                    e.printStackTrace();
                    return null;
                }
            }
            chooser.setSelectedFile(sfile);//次回同じファイルが選択された状況で始めたい
        }
        return sfile;
    }

    //
    //以下はインスタンスのメンバー
    //
    private File file=null;//非公開なのでクラス外からは参照できない
    public String getName()
    {
        if(file!=null)return file.getName();
        else return "TextFile.file=null";
    }
    //===========================ここから===========================
    public String getText()
    {
        if(file==null){
            file=chooseFile(false);
            if(file==null)return null;
        }
        StringBuffer buffer=new StringBuffer();
        BufferedReader input=null;
        try{
            input=new BufferedReader(
                new InputStreamReader(new FileInputStream(file),"JISAutoDetect")
            );
            boolean newLine=false;
            while(input.ready()){
                int c=input.read();
                if(c==-1)break;//ストリームの終端では-1が戻る
                if((char)c=='\r'){ //文字列の改行は\r,\n,\r\nなど複数の基準がある
                    buffer.append("\n");
                    newLine=true;
                }else if((char)c=='\n'){
                    if(newLine)newLine=false;
                    else buffer.append("\n");
                }else{
                    if(newLine)newLine=false;
                    buffer.append((char)c);
                } 
            }
            input.close();input=null;
        }catch(Exception ex){
            //ファイルからのテキストの読み取りで例外が起きたら
            ex.printStackTrace();
            System.out.println("エラー:ファイル["+file.getName()+"]からデータを読めません");
            return null;
        }finally{
            try{
                //閉じていないストリームは閉じる
                if(input!=null)input.close();
            }catch(Exception e2){
                //閉じることに失敗したら打つ手なし
                e2.printStackTrace();
                System.exit(-1);//java仮想機械を戻り値-1で終了
            }
        }
        System.out.println("getText():OK "+file);
        return buffer.toString();
    }
    
    public boolean setText(String str)
    {
        if(file==null){
            file=chooseFile(true);
            if(file==null)return false;
        }else{
            int ret=JOptionPane.showConfirmDialog(/*引数が長いので改行しています*/
                null,
                "既存ファイルを上書きしますか?\n;実行確認をお願いします",
                "上書きによるクリアの確認",
                JOptionPane.OK_CANCEL_OPTION,
                JOptionPane.WARNING_MESSAGE
                );
            if(ret==JOptionPane.CANCEL_OPTION){
                return false;
            }
        }
        PrintWriter output=null;
        ByteArrayInputStream raw_input=null;
        BufferedReader input=null;
        try{
            //改行コードが異なるために少し面倒な処理になっている
            output=
                new PrintWriter(new OutputStreamWriter(new FileOutputStream(file),"Windows-31J"));
            raw_input=new ByteArrayInputStream(str.getBytes());
            input=new BufferedReader(new InputStreamReader(raw_input));
            while(input.ready()){
                int n=input.read();
                if(n==-1)break;//ストリームの終端では-1が戻る
                if((char)n=='\n'){//文字列の改行はjavaでは\nだが
                    output.print("\r\n");//Windowsのテキストでは改行は\r\n
                }else{
                    output.write(n);
                }
            }
            input.close();input=null;
            raw_input.close();raw_input=null;
            output.close();output=null;
        }catch(Exception ex){
            //テキストのファイルへの書き込みで例外が起きたら
            ex.printStackTrace();
            System.out.println("エラー:ファイル["+file.getName()+"]へデータを保存できません");
            return false;
        }finally{
            try{
                //閉じていないストリームはすべて閉じる
                if(input!=null)input.close();
                if(raw_input!=null)raw_input.close();
                if(output!=null)output.close();
            }catch(Exception e2){
                //閉じることに失敗したら打つ手なし
                e2.printStackTrace();
                System.exit(-1);//java仮想機械を戻り値-1で終了
            }
        }
        System.out.println("setText():OK "+file);
        return true;
    }
    //=====================ここまで========================
}

[ prev | next | index ]