[PHP-users 21230]Re: ネストされたタグを正規表現で抽出する場合

Katsuo Mogi mogi-k2 @ msg.biglobe.ne.jp
2004年 4月 11日 (日) 19:59:41 JST


茂木です。

In the message Re: [PHP-users 21209] Re: ネストされたタグを正規表現で抽出す
               <4E0F4F12-8B99-11D8-8319-000A95A991EA @ offside.ne.jp>
Eiji Miwa <miwa @ offside.ne.jp> wrote:

>  こちらの環境では、これで再現できました。

> 	$string = '<body><table cellspacing="0">' . str_repeat ( 'a', 12500 ) 
> . '</table></body>';
> 	preg_match ( 
> '/<table[^>]*>(?:(?>(?:(?!<\/?table[^>]*>).)+)|(?R))+<\/table>/si', 
> $string, $matches );
> 	print_r ( $matches );

再現していただきまして、ありがとうございます。
そうか、str_repeat()使えば簡単でしたね。

許容量が違うものの、やはりどこかでエラーになってしまうんですね。
正規表現の書き方で何とかなればいいですが、
これ以上は改善の余地はなさそうだし。

>  正規表現の限界というよりは、ハードの限界みたいですね。

PHPの正規表現エンジンに対してハードが限界なのだと思いますが、
別の言語(perl)では動き、PHPでは動かないとなるとやっぱり
PCREの限界という表現が妥当ではないかと思うのですが。

tidyも試そうと思いましたが、EUCはサポートされていないようなので
結論としましては、以下の自作関数で切り出すことにしました。

/**
 * 正規表現にマッチする、ネストを許すペアの範囲の文字列を配列で返す
 *
 * tableタグを切り出す場合
 * tagMatchSplit('/<table[^>]*>/si', '/<\/table>/si', $parseString, $matches);
 *
 * @param string  $iOpenRegex      開始文字列の正規表現 '/...regex.../'
 * @param string  $iCloseRegex     終了文字列の正規表現 '/...regex.../'
 * @param string  $iString            対象文字列
 * @param array   &$oMatches          マッチした文字列の配列
 * @return  boolean
 */
function tagMatchSplit($iOpenRegex, $iCloseRegex, $iString, &$oMatches){
  $matchesOpen = $matchesClose = $oMatches = array();
  $start = $pos = $len = $searchPos = $tmpPos = 0;
  $splitstr = '';

  if(!preg_match_all($iOpenRegex, $iString, $matchesOpen)){
    echo 'Open Regex Not Found!! ['.$iString.'] '.__FILE__.' '.__LINE__;
    return false;
  }
  if(!preg_match_all($iCloseRegex, $iString, $matchesClose)){
    echo 'Close Regex Not Found!! ['.$iString.'] '.__FILE__.' '.__LINE__;
    return false;
  }
  if(count($matchesOpen[0]) != count($matchesClose[0])){
    echo 'Regex Pair Unbalance!! ['.$iString.'] '.__FILE__.' '.__LINE__;
    return false;
  }
  for($openStr = array_shift($matchesOpen[0]), $closeStr = array_shift($matchesClose[0]);
      $openStr;
      $openStr = array_shift($matchesOpen[0]), $closeStr = array_shift($matchesClose[0])){
    $start = mb_strpos($iString, $openStr, $pos);
    $len = mb_strpos($iString, $closeStr, $pos) + mb_strlen($closeStr) - $start;
    $searchPos = mb_strlen($openStr);
    //echo "\$pos = $pos; \$start = $start; \$len = $len; \$searchPos = $searchPos<br>";
    while($splitstr = mb_substr($iString, $start, $len)){
      if($nextOpenStr = array_shift($matchesOpen[0])){
        if(($tmpPos = mb_strpos($splitstr, $nextOpenStr, $searchPos)) !== FALSE){
          $nextCloseStr = array_shift($matchesClose[0]);
          $len = mb_strpos($iString, $nextCloseStr, $start + $len) + mb_strlen($nextCloseStr) - $start;
          $searchPos = $tmpPos + mb_strlen($nextOpenStr);
          //echo "\$start = $start; \$len = $len; \$searchPos = $searchPos; \$tmpPos = $tmpPos<br>";
        }else{
          array_unshift($matchesOpen[0], $nextOpenStr);
          break;
        }
      }else{
        break;
      }
    }
    $oMatches[] = $splitstr;
    $pos = $start + $len - 1;
  }
  return true;
}

何だか無駄に手間を掛けてるような気がしますが...。
返信いただいた皆様、ありがとうございました。

--
---------------------------------
 茂木克夫
    mogi-k2 @ msg.biglobe.ne.jp
---------------------------------


PHP-users メーリングリストの案内