如何删除“匹配”括号之间的文本?

时间:2011-05-26 08:31:53

标签: php regex string

当我阅读this XKCD comic的alt(技术标题) - 文本时,我开始好奇维基百科中的每篇文章是否最终都指向哲学文章。所以我开始创建一个Web应用程序,显示它使用PHP“指向”的文章。

(PS:不要担心流量 - 因为我会私下使用它,不会向维基百科服务器发送太多请求)

为此,我必须删除括号和斜体之间的文本,并获取第一个链接。使用PHP Simple HTML DOM Parser可以实现其他目的,但删除括号中的文本是问题..

如果括号中没有括号,那么我可以使用此RegEx:\([^\)]+\),但是,如the article about German language,有些文章重叠了括号(例如:German (Deutsch [ˈdɔʏtʃ] ( listen)) is..),并且以上RegEx无法处理这些情况,因为[^\)]*\)找到第一个结束括号,而不是匹配结束括号。 (实际上,上面的情况不会成为一个问题,因为在两个右括号之间没有文本,但是当两个右括号之间存在链接时,它就成了一个大问题。)

我能想到的一个肮脏的解决方案是:

$s="content of a wikipedia article";$depth=0;$s2="";
for($i=0;$i<strlen($s);$i++){
    $c=substr($s,$i,1);
    if($c=='(')$depth++;
    if($c==')'){if($depth>0)$depth--;continue;}
    if($depth==0) $s2.=$c;
}
$s=$s2;

但是,我不喜欢这个解决方案,因为它将一个字符串减少为单个字符,看起来像是不必要的......

是否有其他方法可以删除一对(匹配)括号中的文字?

例如,我想制作这个文字:

blah(asdf(foo)bar(lol)asdf)blah

进入这个:

blahblah

但不是这样的:

blahbarasdf)blah

编辑:从EmilVikström的回答评论中,我意识到上述方法(删除括号中的文本)可能会删除包含括号的链接。但是,我仍然想要上述问题的答案,因为我之前遇到过类似的问题,我想知道答案......

所以我的问题仍然是:如何在匹配的括号中删除文本?

2 个答案:

答案 0 :(得分:3)

您可以查看recursive patterns,它应该可以解决问题。

当我阅读漫画时,我没有意志力去理解递归模式,所以我简化它以找到一个链接,然后检查它是否在括号中。这是我的解决方案:

  //Fetch links
  $matches = array();
  preg_match_all('!<a [^>]*href="/wiki/([^:"#]+)["#].*>!Umsi', $text, $matches);
  $links = $matches[1];
  //Find first link not within parenthesis
  $found = false;
  foreach($links as $l) {
    if(preg_match('!\([^)]+/wiki/'.preg_quote($l).'.+\)!Umsi', $text)) {
      continue;
    }else{
      $found = true;
      break;
    }
  }

这是我的整个脚本:http://lajm.eu/emil/dump/filosofi.phps

答案 1 :(得分:1)

大!我正在看清有人在清理维基百科纯文本内容时遇到的问题。这是你如何使用它。

cleanBraces("blah(asdf(foo)bar(lol)asdf)blah", "(", ")")

将返回

  

blahblah

您可以传递任何类型的大括号。喜欢[和]或{和}

这是我的源代码。

function cleanBraces($source, $oB, $eB) {
    $finalText = "";
    if (preg_match("/\\$oB.*\\$eB/", $source) > 0) {
        while (preg_match("/\\$oB.*\\$eB/", $source) > 0) {
            $brace = getBracesPos($source, $oB, $eB);
            $finalText .= substr($source, 0, $brace[0]);
            $source = substr($source, $brace[1] + 1, strlen($source) - $brace[1]);
        }
        $finalText .= $source;
    } else {
        $finalText = $source;
    }
    return $finalText;
}

function getBracesPos($source, $oB, $eB) {
    if (preg_match("/\\$oB.*\\$eB/", $source) > 0) {
        $open = 0;
        $length = strlen($source);
        for ($i = 0; $i < $length; $i++) {
            $currentChar = substr($source, $i, 1);
            if ($currentChar == $oB) {
                $open++;
                if ($open == 1) { // First open brace
                    $firstOpenBrace = $i;
                }
            } else if ($currentChar == $eB) {
                $open--;
                if ($open == 0) { //time to wrap the roots
                    $lastCloseBrace = $i;
                    return array($firstOpenBrace, $lastCloseBrace);
                }
            }
        } //for
    } //if
}