[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 メーリングリストの案内