1

这就是问题所在:我有一个数据库,里面满是用 XHTML 标记的文章。我们的应用程序使用Prince XML生成 PDF。其中一个原因是脚注是内联标记的,使用以下模式:

<p>Some paragraph text<span class="fnt">This is the text of a footnote</span>.</p>

Prince 用span.fnt数字脚注标记替换 every,并将随附的文本呈现为页面底部的脚注。

我们希望以电子书格式呈现相同的内容,XHTML 是一个很好的起点,但内联脚注很糟糕。我想要做的是在我的电子书构建脚本中将脚注转换为尾注。

这就是我的想法:

  1. $endnotes创建一个名为存储尾注文本的空数组。
  2. 将变量$endnote_no设置为零。此变量将保存当前尾注编号,以内联方式显示为尾注标记,并用于将尾注标记链接到特定尾注。
  3. 使用preg_replacepreg_replace_callback查找 的每个实例<span class="fnt">(.*?)</span>
  4. $endnote_no每个实例的增量,并将内联span替换为'<sup><a href="#endnote_' 。$尾注_没有。'">' .$endnote_no . ''`
  5. 将脚注文本推送到$endnotes数组中,以便我可以在文档末尾使用它。
  6. 在用数字尾注引用替换所有脚注后,遍历$endnotes数组以将尾注作为 XHTML 中的有序列表输出。

这个过程有点超出我的 PHP 理解范围,当我尝试将其转换为代码时,我迷失了方向。这是我到目前为止所拥有的,我主要根据我在 PHP 文档中找到的代码示例拼凑而成:

$endnotes = array();
$endnote_no = 0;
class Endnoter {

  public function replace($subject) {
    $this->endnote_no = 0;
    return preg_replace_callback('`<span class="fnt">(.*?)</span>`', array($this, '_callback'), $subject);
  }

  public function _callback($matches) {
    array_push($endnotes, $1);
    return '<sup><a href="#endnote_' . $this->endnote_no++ . '">' . $this->endnote_no . '</a></sup>';
  }
}

...

$replacer = new Endnoter();
$replacer->replace($body);
echo '<pre>';
print_r($endnotes); // Just checking to see if the $endnotes are there.
echo '</pre>';

任何指导都会有所帮助,尤其是如果有更简单的方法可以到达那里。

4

2 回答 2

2

首先,最好不要使用正则表达式进行 HTML 操作;请参见此处: 如何在 PHP 中解析和处理 HTML/XML?

但是,如果您真的想走这条路,那么您的代码有一些问题:

  1. return '<sup><a href="#endnote_' . $this->endnote_no++ . '">' . $this->endnote_no . '</a></sup>';
    

    如果 endnote_no 为 1,例如这将产生

    '<sup><a href="#endnote_1">2</a></sup>';
    

    如果这些值都应该是相同的,你想先增加 endnote_no :

    return '<sup><a href="#endnote_' . ++$this->endnote_no . '">' . $this->endnote_no . '</a></sup>';
    

    注意调用前面的 ++ 而不是之后。

  2. array_push($endnotes, $1);
    

    $1不是一个定义的值。你正在寻找你传递给回调的数组,所以你想要$matches[1]

  3. print_r($endnotes);
    

    $endnotes没有在类之外定义,因此您要么想要一个 getter 函数来检索$endnotes通常更可取),要么在类中公开变量。使用吸气剂:

    class Endnotes {
        private $endnotes = array();
        //replace any references to $endnotes in your class with $this->endnotes and add a function:
    
        public function getEndnotes() {
            return $this->endnotes;
        }
    }
    //and then outside
    print_r($replacer->getEndnotes());
    
  4. preg_replace_callback不通过引用传递,因此您实际上并没有修改原始字符串。$replacer->replace($body);应该是$body = $replacer->replace($body);,除非您想通过引用将 body 传递给replace()函数并在那里更新其值。

于 2013-08-30T21:43:11.453 回答
2

不知道更简单的方法,但你已经完成了一半。这似乎有效。

我只是稍微清理了一下,在你的类中移动了变量并添加了一个输出方法来获取脚注列表。

class Endnoter
{
    private $number_of_notes = 0;
    private $footnote_texts = array();

    public function replace($input) {

        return preg_replace_callback('#<span class="fnt">(.*)</span>#i', array($this, 'replace_callback'), $input);

    }

    protected function replace_callback($matches) {

        // the text sits in the matches array
        // see http://php.net/manual/en/function.preg-replace-callback.php
        $this->footnote_texts[] = $matches[1];

        return '<sup><a href="#endnote_'.(++$this->number_of_notes).'">'.$this->number_of_notes.'</a></sup>';

    }

    public function getEndnotes() {
        $out = array();
        $out[] = '<ol>';

        foreach($this->footnote_texts as $text) {
            $out[] = '<li>'.$text.'</li>';
        }

        $out[] = '</ol>';

        return implode("\n", $out);
    }

 }
于 2013-08-30T21:52:25.947 回答