【问题标题】:How can I replace each URL in a string with another unique URL?如何用另一个唯一 URL 替换字符串中的每个 URL?
【发布时间】:2010-10-20 06:27:14
【问题描述】:

我有以下几点:

$reg[0] = '`<a(\s[^>]*)href="([^"]*)"([^>]*)>`si';
$reg[1] = '`<a(\s[^>]*)href="([^"]*)"([^>]*)>`si';
$replace[0] = '<a$1href="http://www.yahoo.com"$3>';
$replace[1] = '<a$1href="http://www.live.com"$3>';
$string = 'Test <a href="http://www.google.com">Google!!</a>Test <a href="http://www.google.com">Google!!2</a>Test';
echo preg_replace($reg, $replace, $string);

结果:

Test <a href="http://www.live.com">Google!!</a>Test <a href="http://www.live.com">Google!!2</a>Test

我希望最终得到(不同之处在于第一个链接):

Test <a href="http://www.yahoo.com">Google!!</a>Test <a href="http://www.live.com">Google!!2</a>Test

这个想法是用唯一的其他 URL 替换字符串中链接中的每个 URL。这是一个通讯系统,我想跟踪人们点击了什么,所以 URL 将是一个“假” URL,在记录点击后他们将被重定向到真实 URL。

【问题讨论】:

    标签: php regex preg-replace replace


    【解决方案1】:

    我不知道我是否理解正确。但我写了以下sn-p: 正则表达式匹配一些超链接。然后循环 通过结果并将文本节点与超链接引用进行比较。 当在超链接引用中找到文本节点时,它会通过插入带有唯一键的引用示例链接来扩展匹配项。

    更新 sn-ps 查找所有超链接:

    1. 查找链接
    2. 建立回溯链接
    3. 找到每个找到的链接的位置(匹配[3])和 设置模板标签
    4. 用引用链接替换模板标签 每个链接位置都是唯一的。

    $string = '

    通讯名称

    Lorem ipsum dolor sit amet,consectetur adipiscing elit。 Donec lobortis, ligula sed sollicitudin dignissim, lacus dolor suscipit sapien, bar.com ipsum ligula 非侵权。 Quisque sagittis sodales 精英。 Mauris dictum blandit lacus。 Mauris consequat laoreet lacus.

    通讯名称

    Lorem ipsum dolor sit amet,consectetur adipiscing elit。 Donec lobortis, ligula sed sollicitudin dignissim, lacus dolor suscipit sapien, bar.com ipsum ligula 非侵权。 Quisque sagittis sodales 精英。 Mauris dictum blandit lacus。 Mauris consequat laoreet lacus.

    通讯名称

    Lorem ipsum dolor sit amet,consectetur adipiscing elit。 Donec lobortis, ligula sed sollicitudin dignissim, lacus dolor suscipit sapien, bar.com ipsum ligula 非侵权。 Quisque sagittis sodales 精英。 Mauris dictum blandit lacus。 Mauris consequat laoreet lacus.

    ';
    $regex = '<[^>]+>(.*)<\/[^>]+>';
    preg_match_all("'<a\s+href=\"(.*)\"\s*>(.*)<\/[^>]+>'U",$string,$matches);
    
    
    $uniqueURL = 'http://www.yourdomain.com/trackback.php?id=';
    
    foreach($matches[2] as $k2 => $m2){
        foreach($matches[1] as $k1 => $m1){
            if(stristr($m1, $m2)){
                    $uniq = $uniqueURL.md5($matches[0][$k2])."_".rand(1000,9999);
                    $matches[3][$k1] = $uniq."&refLink=".$m1;
            }
        }
    }
    
    
    foreach($matches[3] as $key => $val) {
    
        $startAt = strpos($string, $matches[1][$key]);
        $endAt= $startAt + strlen($matches[1][$key]);
    
        $strBefore = substr($string,0, $startAt);
        $strAfter = substr($string,$endAt);
    
        $string = $strBefore . "@@@$key@@@" .$strAfter;
    
    }
    foreach($matches[3] as $key => $val) {
            $string = str_replace("@@@$key@@@",$matches[3][$key] ,$string);
    }
    print "<pre>";
    echo $string;
    

    【讨论】:

    • 这一直有效,直到您在同一段文本中有 2 个链接指向同一个地方 (bar.com),但您希望每个链接都有唯一的 url。您的数组将包含唯一的 url,但是如何在字符串中替换它们?
    【解决方案2】:

    在 PHP 5.3 之前,您只能在现场创建函数,您必须使用 create_function(我讨厌它)或辅助类。

    /**
     * For retrieving a new string from a list.
     */
    class StringRotation {
        var $i = -1;
        var $strings = array();
    
        function addString($string) {
            $this->strings[] = $string;
        }
    
        /**
         * Use sprintf to produce result string
         * Rotates forward
         * @param array $params the string params to insert
         * @return string
         * @uses StringRotation::getNext()
         */
        function parseString($params) {
            $string = $this->getNext();
            array_unshift($params, $string);
            return call_user_func_array('sprintf', $params);
        }
    
        function getNext() {
            $this->i++;
            $t = count($this->strings);
            if ($this->i > $t) {
                $this->i = 0;
            }
            return $this->strings[$this->i];
        }
    
        function resetPointer() {
            $this->i = -1;
        }
    }
    
    $reg = '`<a(\s[^>]*)href="([^"]*)"([^>]*)>`si';
    $replaceLinks[0] = '<a%2$shref="http://www.yahoo.com"%4$s>';
    $replaceLinks[1] = '<a%2$shref="http://www.live.com"%4$s>';
    
    $string = 'Test <a href="http://www.google.com">Google!!</a>Test <a href="http://www.google.com">Google!!2</a>Test';
    
    $linkReplace = new StringRotation();
    foreach ($replaceLinks as $replaceLink) {
        $linkReplace->addString($replaceLink);
    }
    
    echo preg_replace_callback($reg, array($linkReplace, 'parseString'), $string);
    

    【讨论】:

      【解决方案3】:

      我不擅长使用正则表达式,但如果您正在做的只是将外部 URL(即不是您的站点/应用程序的一部分)替换为将跟踪点击并重定向用户的内部 URL,那么它应该很容易构造一个只匹配外部 URL 的正则表达式。

      假设您的域是foo.com,那么您只需要创建一个正则表达式,它只匹配不包含以@987654321@ 开头的URL 的超链接。现在,正如我所说,我对正则表达式非常不满意,但这是我最好的尝试:

      $reg[0] = '`<a(\s[^>]*)href="(?!http://foo.com)([^"]*)"([^>]*)>`si';
      

      编辑: 如果您还想跟踪对内部 URL 的点击,那么只需将 @987654322@ 替换为您的重定向/跟踪页面的 URL,例如@987654323@.

      我将通过一个示例场景来说明我在说什么。假设您有以下时事通讯:

      <h1>Newsletter Name</h1>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis,
      ligula <a href="http://bar.com">sed sollicitudin</a> dignissim, lacus dolor
      suscipit sapien, <a href="http://foo.com">eget auctor</a> ipsum ligula
      non tortor. Quisque sagittis sodales elit. Mauris dictum blandit lacus.
      Mauris consequat <a href="http://last.fm">laoreet lacus</a>.</p>
      

      就本练习而言,搜索模式将是:

      // Only match links that don't begin with: http://foo.com/out.php
      `<a(\s[^>]*)href="(?!http://foo.com/out\.php)([^"]*)"([^>]*)>`si
      

      这个正则表达式可以分解为 3 个部分:

      1. &lt;a(\s[^&gt;]*)href="
      2. (?!http://foo.com/out\.php)([^"]*)
      3. "([^&gt;]*)&gt;

      在第一次搜索时,脚本将检查:

      <a href="http://bar.com">
      

      此链接满足正则表达式的所有 3 个组件,因此 URL 存储在数据库中并替换为 @987654324@

      在第二次搜索时,脚本将检查:

      <a href="http://foo.com/out.php?id=1">
      

      此链接匹配 1 和 3,但不匹配 2。因此搜索将转到下一个链接:

      <a href="http://foo.com">
      

      此链接满足正则表达式的所有 3 个组件,因此 URL 存储在数据库中并替换为 @987654325@

      在搜索的第 3 遍时,脚本将检查前 2 个(已替换)链接,跳过它们,然后找到与时事通讯中最后一个链接的匹配项。

      【讨论】:

      • 内部或外部对我来说并不重要。我想替换所有链接以跟踪所有点击。
      • 在这种情况下,您只需将foo.com 替换为重定向/跟踪页面的确切地址。
      • 如果您有 1 个 url 去 www.google.com 而另一个去 cnn.com,那仍然行不通。每个链接都需要替换为唯一的其他链接。
      • 这基本上是我所做的,但它不起作用,问题是 PHP 无法仅在第一次找到 reg exp 时替换 - 至少我知道。它将替换所有找到的字符串。
      • 我无法比上次编辑更清楚地解释它。请注意我正在使用的正则表达式与您正在使用的正则表达式之间的区别。这和你正在做的不一样。这种模式可以区分替换链接和未替换链接。
      【解决方案4】:

      问题是你的第一个替换字符串将被第二个搜索模式匹配,有效地用第二个替换字符串覆盖第一个替换字符串。

      除非您能以某种方式将“修改过的”链接与原始链接区分开来,以免它们被其他表达式捕获(也许通过添加额外的 HTML 属性?),否则我认为您无法真正解决这个问题一个preg_replace() 电话。想到的一种可能的解决方案(除了正则表达式中的差异)是使用preg_match_all(),因为它会为您提供一系列匹配项。然后,您可以通过遍历数组并在每个匹配的 URL 上运行 str_replace() 来使用跟踪 URL 对匹配的 URL 进行编码。

      【讨论】:

      • 如何使用 preg_match 进行替换?
      • 抱歉,我最初在写帖子时忘记了这一点——我编辑了帖子以添加一种使用 preg_match() 实现您想要的潜在方法。希望对您有所帮助。
      • 另外,我的意思是 preg_match_all() 时不小心说了 preg_match()——抱歉,我已经有一段时间没有使用这些函数了。
      猜你喜欢
      • 1970-01-01
      • 2012-07-17
      • 2013-12-03
      • 2021-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多