括弧・タグ内文字列の解析

HTMLタグや括弧は、「開始」文字(</()と「終端」文字(>/))内に「内容」が入っています。タグや括弧の内部にある内容を取り出すには、開始文字と終端文字の「間」にある文字列を抽出するわけですね。今回は、この開始文字と終端文字の認識、そしてその間にある文字列を抽出する例として文字列内にある括弧内文字列を取り出してみましょう。取り出す対象は、「一番外側」の括弧内とします。例えば(12+(3+1)*4)などと入れ子になっている場合は、内側の括弧はそのままで12+(3+1)*4を取り出すわけです。

開始・終端文字の対応検査

 まず、解析を行う前に開始文字と終端文字が正しく対応しているか、つまり開始文字列が正しく閉じれられ、開始文字に対応しない終端文字がないか、調べてみます。この段階では、たとえば

((1+3)
())4+3(34+11)*2
)123(

 といった開始と終端が対応していない文字列を検出するわけです。開始文字と終端文字の対応が正しい文字列は、開始文字が必ず終端文字で閉じられる文字列ですから、

  1. 開始文字と終端文字は数が等しい
  2. 文字列中、いかなる場所でも終端文字の数はこれまでに出てきた開始文字の数を超えない

 という条件を満たす文字列になりますね。今回は、この条件を満たしているか調べるため開始文字と終端文字にそれぞれ1、−1という値を持たせ、文字列中にこれらの文字が出てきたらその値を「加算」して行くことにしました。これで、最後に加算した結果が0になれば、開始文字(1)と終端文字(−1)の合計が0になるわけですから、開始文字と終端文字は等しいわけでまず1の条件を満たすことになります。また、文字列の操作中、加算結果がマイナスにならなければそれは終端文字(−1)が開始文字(1)を上回らなかったことになるので、2の条件も満たすわけです。逆に言うと、最終的な加算結果が0にならなかったり途中でマイナスになればエラーということにすれば良いでしょう。

 次の例では、文字列stArgに対して括弧のチェックを行っています。

  for (i=0;i<stArg.length();i++) {

      if (stArg.charAt(i)=='(')
          k++;

      if (stArg.charAt(i)==')')
          k--;

      if (k<0)
          break;

  }

  if (k!=0)
      return null;

文字列抽出

 では、続いて開始文字と終端文字の間にある文字列を抽出してみます。考え方は、検査と同じようなもので、文字列の先頭から走査して開始文字列を見つけたら「その開始文字に対応する終端文字までの部分文字列」を切り出すだけですね。今回は一番「外側」の開始・終端文字列を処理対象とするので、その間の区間は開始文字を1、終端文字をー1とする値が0でない区間、ということになります(すでに先の検査で、一番外側の開始・終端文字に対する対応が取れている、つまり一番外側の開始・終端文字の「外側」の値は全区間で0であることが保障されている)。

 具体的な処理としては、文字列の先頭から

  1. 開始文字を探す
  2. 開始文字が見つかったら、その開始文字列に対応する終端文字を探す
  3. 開始文字と終端文字の間にある文字列を取得

 という処理を行うことになるでしょう。2の処理は、走査対象の位置j(初期値は開始文字の位置i)にある文字が開始文字なら変数nを+1、終端文字ならー1する処理をnが0になるまで繰り返せばjに終端文字の位置が求まります。

  while (n!=0) { // 括弧の終端探索

      if (stArg.charAt(++j)==')')
          n--;

      if (stArg.charAt(j)=='(')
          n++;

  }

 終端文字の位置がjに求まったら、stArg.substring(i+1,j)で開始文字から終端文字までの文字列を取得できます。今回は、こうして見つけた文字列をVectorに格納するようにしました。

  for (i=0;i<stArg.length();i++)
      if (stArg.charAt(i)=='(') {

          int j=i,n=1;

          while (n!=0) { // 括弧の終端探索

              if (stArg.charAt(++j)==')')
                  n--;

              if (stArg.charAt(j)=='(')
                  n++;

          }

          String stWrk=new String();

          stWrk=stArg.substring(i+1,j);

          vtList.addElement(stWrk);

          i=j;

      }

 以上のことをまとめると、引数で渡された文字列stArgの括弧内にある部分文字列のVectorを返すメソッドparse()は以下のようになります。

  private Vector parse(String stArg) {

      Vector vtList=new Vector();

      int k=0,i;

      for (i=0;i<stArg.length();i++) { // 括弧の対応検査

          if (stArg.charAt(i)=='(')
              k++;

          if (stArg.charAt(i)==')')
              k--;

          if (k<0)
              break;

      }

      if (k!=0)
          return null;

      for (i=0;i<stArg.length();i++)
          if (stArg.charAt(i)=='(') {

              int j=i,n=1;

              while (n!=0) { // 括弧の終端探索

                  if (stArg.charAt(++j)==')')
                      n--;

                  if (stArg.charAt(j)=='(')
                      n++;

              }

              String stWrk=new String();

              stWrk=stArg.substring(i+1,j);

              vtList.addElement(stWrk);

              i=j;

          }

      return vtList;

  }

プログラム

 String欄に適当な括弧付文字列を入れ、ボタンをクリックしてみてください。一番外側のカッコ内にある文字列を下のテキストエリアに表示します。以下のような文字列で試してみると良いでしょう。

((123)aaa)
(123)456(789)012(34)56
((333)))(33

プログラムソース表示


プログラミング資料庫 > 数学アルゴリズム演習ノート