[Work/Class/Java with Processing/4_SwingInProcessing]

アート系学生のためのJAVA with Processing - その4-4 JavaのStringクラスを使って文字列処理

Stringクラスの必要性と利便性

ActionCommandや,これからネットワーク越しのメッセージなどを扱う時に,JavaのStringクラスやその類似クラスによる文字列操作は避けて通れない.

が,JavaのStringは他言語の標準ライブラリに比べて異様なまでに便利であり,標準で備えていない機能はないとも言えるぐらいである.ActionCommandやネットワークメッセージに使う基本的な機能のみを紹介し,あとはJavaの公式ドキュメントを紹介する.

Java公式のStringクラスの仕様 https://docs.oracle.com/javase/jp/8/docs/api/java/lang/String.html

コンストラクタ

String myString = new String("初期化の際の文字列");で最初から「初期化の際の文字列」が入ったStringのインスタンスが生成できる.

同じ文字列かどうかを見る

myString.euqals("比較したい文字列");を実行すると,同じ文字列(同じオブジェクトインスタンスではない)ならばtrueが,そうでないならfalseが返ってくる.これを利用し,if文の中に突っ込むことで,これまでActionCommand等の判定を行ってきた.

文字列同士の連結

String firstString = new String("FirstString");
String secondString = new String("SecondString");

が予め存在する時,以下の方法で文字列連結ができる.

String newString = firstString + secondString;
String newString = firstString.concat(secondString);

が,これはfor文やイテレータなど,配列に入っている文字列をぶん回す際には使えないので,StringBufferクラスやStringBuilderクラスを使って以下の方法がある.

StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(firstString);
stringBuffer.append(secondString);
String newString = stringBuffer.toString();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(firstString);
stringBuilder.append(secondString);
String newString = stringBuilder.toString();

速度としてはStringBuilderクラスを使う方法が一番早く(プラス演算子の1万倍ぐらい早い),次いでStringBufferクラスを使う方法が早い(プラス演算子の5千倍ぐらい早い)に.プラス演算子やconcatは極端に遅いが,足さなければならないStringのインスタンスが少なく量が決まっている場合にはコードが短くなるという利点がある.

スプリッタ文字を指定した切り分け

String givenCommand = "OSC/Desafinado/Toalka/Pikka";

という文字列があった場合,「/」を取り除いて各要素を含んだ配列にし,その配列を用いて状態分岐ツリーを作りたい状況が,特にアクションコマンドやネットワークメッセージを利用している場合に多発する.

String[] splittedCommand = givenCommand.split("/", 0)

と書いてやると,この場合は,"OSC", "Desafinado", "Toalka", "Pikka"という文字列(String型)を要素に持つ配列splittedCommandが生成される.分割に使う文字は

2番目の引数は「最大分割数」なのだが,2番目の引数を省略,もしくは0にすることにより全ての要素の分割が行われる.

2番目の引数に「-1」を指定することにより,空白の要素を除くことができる.例えば,

String givenCommand = "OSC,,Desafinado,Talka,,";

という文字列が与えられた時,2番目の引数が0の場合,

givenCommand.split(",", 0);

配列の中身は,"OSC", "", "Desafinado", "Talka", "", ""となり,空白のStringが入ってしまう.そこで-1を第2引数に指定し,

givenCommand.split(",", -1);

とすると,出力の配列の中身は,"OSC", "Desafinado", "Talka"の3要素のみとなる.

文字列から数字型へ変換

数字から文字列への変換は,単に"文字列" + iのように+演算子で既存の文字列につなぎ合わせることで可能である他,

int number = 1356;
String value = new String(number);

のように,Stringのコンストラクタに数字を渡す方法でも可能である.

またIntegerクラスのオブジェクトインスタンスを作り,インスタンス関数toString()を実行することでも,数字の文字列を得られる.

int number = 1356;
Integer myInteger = new Integer(number);
String value = myInteger.toString();

問題は逆で,文字列として受け取った「数字」を演算可能にするために,例えば整数型などに入れる場合である.整数型ならIntegerクラスのstaticメソッドparseInt()を使う.

String commandNumberInString = "1356";
int commandNumberInInteger = Integer.parseInt(commandNumberInString);

と書くと,int型に変換される.

parseInt()staticメソッドであり,クラスのインスタンスを作っていなくても使うことができる.

StringとJTextFieldを使った実例

前項の複数のボタンでテキストフィールドの内容を書き換えるプログラムを,改造し,テキストフィールドに入力された「,」で区切られた2つの数字を足したり引いたりするプログラム.

import processing.awt.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

void setup(){
  size(640, 480);
  Canvas canvas = (Canvas)surface.getNative();
  JLayeredPane layeredPane = (JLayeredPane)canvas.getParent().getParent();
  
  JTextField textField = new JTextField("3,4");
  textField.setBounds(10, 360, 620, 20);

  MyButtonListener myButtonListener = new MyButtonListener();
  myButtonListener.setTextField(textField);

  JButton button1 = new JButton("Add");
  button1.setBounds(10, 390, 100, 20);
  
  JButton button2 = new JButton("Sub");
  button2.setBounds(120, 390, 100, 20);
  
  button1.setActionCommand("add_two_values");
  button1.addActionListener(myButtonListener);
  
  button2.setActionCommand("sub_two_values");
  button2.addActionListener(myButtonListener);
  
  layeredPane.add(textField);
  layeredPane.add(button1);
  layeredPane.add(button2);


  // ボタンリスナーのインナークラス
  // 例によって,インナークラスではなく別ファイルに分離することもできる
  class MyButtonListener implements ActionListener {
      JTextField textField;
  
      void setTextField(JTextField textField){
	  // テキストフィールドを操作するために,変数に実体を登録しておく
	  this.textField = textField;
      }
  
      @Override
      public void actionPerformed(ActionEvent e){
	  String actionCommand = e.getActionCommand();
	  
	  if(actionCommand.equals("add_two_values")){
	      // 足し算ボタンが押された時
	      
	      // JTextFieldクラスのインスタンスが持つgetText関数を使うと
	      // その時点でテキストフィールドに入力されている文字列を
	      // String型のインスタンスとして取り出すことができる
	      String givenString = textField.getText();

	      // 試しに表示してみる
	      System.out.println(givenString);

	      // テキストフィールドに入っていた2つの数字は,間を「,」で区切って入力する
	      // 一つの文字列として入って来ているので,「,」でsplitする
	      String[] givenValues = givenString.split(",", -1);

	      // Integerクラスが持つ静的関数parseInt関数に「数字の文字列」を入れると
	      // int型に変換してくれる
	      int firstValue = Integer.parseInt(givenValues[0]);
	      int secondValue = Integer.parseInt(givenValues[1]);

	      int answer = firstValue + secondValue;
	      // 足したint型の数字を,
	      // Stringクラスの静的関数valueOfに入れるとString型として出力してくれる
	      textField.setText(String.valueOf(answer));
	  }
	  else if(actionCommand.equals("sub_two_values")){
	      // 引き算の場合も,処理は足し算と同じ
	      String givenString = textField.getText();
	      System.out.println(givenString);
	      
	      String[] givenValues = givenString.split(",", -1);
	      int firstValue = Integer.parseInt(givenValues[0]);
	      int secondValue = Integer.parseInt(givenValues[1]);
	      int answer = firstValue - secondValue;
	      textField.setText(String.valueOf(answer));
	  }
      }   
  } //インナークラスここまで
}