PHPで文字列のマッチング

正規表現などを使って文字列をマッチングさせてデータを抽出する方法を確認しています。ここでは、HTMLのテーブルデータ内からデータを抽出する場合を例に確認しました。
 
なお、日本語対応、マルチバイト文字列、文字コードによってはmbstringの正規表現を使用する必要があります。
http://php.net/manual/ja/book.mbstring.php

※目次をクリックすると目次の下部にコンテンツが表示されます。

サンプルデータ
〇ヒアドキュメント
$str = <<〇サンプルデータ
・ヒアドキュメントを使ってサンプルデータを定義。

final String str = """
・・・
<a href="#href1" class="#class1">リンク1</a>
<a href="#href2" class="#class2">リンク2</a>
<a href="#href3" class="#class3">リンク3</a>
<table border="1" cellspacing="0" cellpadding="5" width="50%" class="datatable">
  <tbody>
    <tr>
      <th>
        <p align="center">町丁名</p>
      </th>
      <th>
        <p align="center">男</p>
      </th>
      <th>
        <p align="center">女</p>
      </th>
      <th>
        <p align="center">合計</p>
      </th>
    </tr>
    <tr>
      <td>
        <p align="right">芝一丁目</p>
      </td>
      <td>
        <p align="right">722</p>
      </td>
      <td>
        <p align="right">735</p>
      </td>
      <td>
        <p align="right">1,457</p>
      </td>
    </tr>
・・・
    <tr>
      <td>
        <p align="right">合計</p>
      </td>
      <td>
        <p align="right">6,378</p>
      </td>
      <td>
        <p align="right">6,760</p>
      </td>
      <td>
        <p align="right">12,138</p>
      </td>
    </tr>
  </tbody>
</table>
EOD;

正規表現でパターンマッチして文字列抽出
●正規表現について
 
・ここでは、Perl互換の正規表現(pcre)を使用しています。
http://php.net/manual/ja/pcre.pattern.php
 
●preg_match_all
http://php.net/manual/ja/function.preg-match-all.php
 
〇構文
int preg_match_all ( string $pattern , string $subject [, array &$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]]] )
・subjectを検索し、patternに指定した正規表現にマッチしたすべての文字列を、flagsで指定した順番で、matches(多次元配列)に代入する。
・正規表現にマッチすると、そのマッチした文字列の後から 検索が続行される。
 
〇flags
・PREG_PATTERN_ORDER
$matches[0]はパターン全体にマッチした文字列の配列、 $matches[1]は第1のキャプチャ用サブパターンにマッチした文字列の配列、といった順番となる。
・PREG_SET_ORDER
$matches[0]は1回目のマッチングでキャプチャした値の配列、$matches[1]は2回目のマッチングでキャプチャした値の配列、といった順序となる。
 
例1)

preg_match_all("/<a\s.*?<\/a>/", $str, $matches1, PREG_SET_ORDER);
foreach ($matches1 as $val) {
  print $val[0] . "\n";
}
↓
<a href="#href1" class="#class1">リンク1</a>
<a href="#href2" class="#class2">リンク2</a>
<a href="#href3" class="#class3">リンク3</a>

 
例2)正規表現内に括弧を使用する場合
・マッチした文字列の中からさらに部分的に文字列を抽出する場合は正規表現に丸括弧を指定する。

preg_match_all("/<a\shref=\"(.*?)\".*>(.*?)<\/a>/", $str, $matches2, PREG_SET_ORDER);
foreach ($matches2 as $val) {
  print "href:{$val[1]},リンク名:{$val[2]}\n";
}
↓
href:#href1,リンク名:リンク1
href:#href2,リンク名:リンク2
href:#href3,リンク名:リンク3

 
●改行文字を含めてマッチングさせたい場合
 
・パターン修飾子に”s”を指定する。
http://php.net/manual/ja/reference.pcre.pattern.modifiers.php
 
例3)
preg_match_all(“/<tbody>(.*?)<\/tbody>/s”, $str, $a_tbody, PREG_SET_ORDER);

正規表現を使って文字列を置換
●preg_replace
http://php.net/manual/ja/function.preg-replace.php
 
〇構文
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
・subjectに関してpatternを用いて検索を行い、replacementに置換する。
・subject引数が配列の場合は配列を、その他の場合は文字列を返す。
 
例4)
preg_match_all(“/<tbody>(.*?)<\/tbody>/s”, $str, $a_tbody, PREG_SET_ORDER);
$s_tbody = preg_replace(‘/(<p align=”right”>|\r\n|\r|\n|\s|<p>|<\/p>| )/’, “”, $a_tbody[0][1]);

配列、文字列の存在確認
上記4)の例では、$str文字列に対するpreg_match_allメソッドの結果(多次元配列)に対して、参照、preg_replace関数を実行しています。
 
1行目でパターンがマッチされなかった場合は、$a_tbodyは空の配列となるため、$preg_replace関数実行前に空のチェック、文字列のチェックなどを行っています。
 
以下、変数の空のチェック、文字列のチェックなどの方法のいくつかを示します。
 
①empty
http://php.net/manual/ja/function.empty.php
 
〇構文
bool empty ( mixed $var )
・変数が空であるかどうかを検査する。
・var が存在し、かつその値が空や0でなければ FALSE を返す。
・次のような値は空であるとみなされる。
“” (空文字列)、0 (整数 の 0)、0.0 (浮動小数点数の 0)、”0″ (文字列 の 0)、
NULL、FALSE、array() (空の配列)、$var; (変数が宣言されているが、値が設定されていない)
 
②is_string
http://php.net/manual/ja/function.is-string.php
 
〇構文
bool is_string ( mixed $var )
・指定した変数の型が文字列かどうかを調べる。

正規表現を使ってパターンマッチして真偽の判定
●preg_match
http://php.net/manual/ja/function.preg-match.php
 
〇構文
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
・patternで指定した正規表現によりsubjectを検索する。
・マッチした場合に1を返す。マッチしなかった場合は0、エラーが発生した場合はFALSEを返す。
 
例)テーブル内のtdタグ内のデータを抽出するが、項目名に(総数|合計|計)が含まれる行のデータは除外する

preg_match_all("/<tbody>(.*?)<\/tbody>/s", $str, $a_tbody, PREG_SET_ORDER);
if (!empty($a_tbody)){
  if (is_string($a_tbody[0][1])){
    $s_tbody = preg_replace('/(<p align="right">|\r\n|\r|\n|\s|<p>|<\/p>| )/', "", $a_tbody[0][1]);
    preg_match_all("/<tr>(.*?)<\/tr>/s", $s_tbody, $a_tr, PREG_SET_ORDER);
    foreach ($a_tr as $val) {
      preg_match_all("/<td>(.*?)<\/td>/s", $val[1], $a_td, PREG_SET_ORDER);
      if (!empty($a_td)){
        if ( !preg_match("/(総数|合計|計)/", $a_td[0][1])) {
          print "{$a_td[0][1]}の男は{$a_td[1][1]}人、女は{$a_td[2][1]}人、合計{$a_td[3][1]}人です。\n";
        }
      }
    }
  }
}

関連記事の目次

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です