4

preg_match_all()から返された$ matchs配列を使用して、件名の文字列を強調表示しようとしています。例から始めましょう:

preg_match_all("/(.)/", "abc", $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);

これは戻ります:

Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [0] => a
                    [1] => 0
                )

            [1] => Array
                (
                    [0] => a
                    [1] => 0
                )

        )

    [1] => Array
        (
            [0] => Array
                (
                    [0] => b
                    [1] => 1
                )

            [1] => Array
                (
                    [0] => b
                    [1] => 1
                )

        )

    [2] => Array
        (
            [0] => Array
                (
                    [0] => c
                    [1] => 2
                )

            [1] => Array
                (
                    [0] => c
                    [1] => 2
                )

        )

)

この場合に私がしたいのは、全体的な消費データと各後方参照を強調表示することです。

出力は次のようになります。

<span class="match0">
    <span class="match1">a</span>
</span>
<span class="match0">
    <span class="match1">b</span>
</span>
<span class="match0">
    <span class="match1">c</span>
</span>

もう一つの例:

preg_match_all("/(abc)/", "abc", $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);

戻る必要があります:

<span class="match0"><span class="match1">abc</span></span>

これが十分に明確であることを願っています。

全体的に消費されたデータを強調表示し、各後方参照を強調表示したいと思います。

前もって感謝します。不明な点がございましたら、お問い合わせください。

注:htmlを壊してはなりません。正規表現と入力文字列は、コードでは不明であり、完全に動的です。したがって、検索文字列はhtmlにすることができ、一致するデータにはhtmlのようなテキストを含めることができます。

4

4 に答える 4

3

これは、これまでに投げたすべての例で正しく動作するようです。他の状況で再利用できるように、HTMLマングリング部分から抽象的な強調表示部分を切り離していることに注意してください。

<?php

/**
 * Runs a regex against a string, and return a version of that string with matches highlighted
 * the outermost match is marked with [0]...[/0], the first sub-group with [1]...[/1] etc
 *
 * @param string $regex Regular expression ready to be passed to preg_match_all
 * @param string $input
 * @return string
 */
function highlight_regex_matches($regex, $input)
{
    $matches = array();
    preg_match_all($regex, $input, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);

    // Arrange matches into groups based on their starting and ending offsets
    $matches_by_position = array();
    foreach ( $matches as $sub_matches )
    {
            foreach ( $sub_matches as $match_group => $match_data )
            {
                    $start_position = $match_data[1];
                    $end_position = $start_position + strlen($match_data[0]);

                    $matches_by_position[$start_position]['START'][] = $match_group;

                    $matches_by_position[$end_position]['END'][] = $match_group;
            }
    }

    // Now proceed through that array, annotoating the original string
    // Note that we have to pass through BACKWARDS, or we break the offset information
    $output = $input;
    krsort($matches_by_position);
    foreach ( $matches_by_position as $position => $matches )
    {
            $insertion = '';

            // First, assemble any ENDING groups, nested highest-group first
            if ( is_array($matches['END']) )
            {
                    krsort($matches['END']);
                    foreach ( $matches['END'] as $ending_group )
                    {
                            $insertion .= "[/$ending_group]";
                    }
            }

            // Then, any STARTING groups, nested lowest-group first
            if ( is_array($matches['START']) )
            {
                    ksort($matches['START']);
                    foreach ( $matches['START'] as $starting_group )
                    {
                            $insertion .= "[$starting_group]";
                    }
            }

            // Insert into output
            $output = substr_replace($output, $insertion, $position, 0);
    }

    return $output;
}

/**
 * Given a regex and a string containing unescaped HTML, return a blob of HTML
 * with the original string escaped, and matches highlighted using <span> tags
 *
 * @param string $regex Regular expression ready to be passed to preg_match_all
 * @param string $input
 * @return string HTML ready to display :)
 */
function highlight_regex_as_html($regex, $raw_html)
{
    // Add the (deliberately non-HTML) highlight tokens
    $highlighted = highlight_regex_matches($regex, $raw_html);

    // Escape the HTML from the input
    $highlighted = htmlspecialchars($highlighted);

    // Substitute the match tokens with desired HTML
    $highlighted = preg_replace('#\[([0-9]+)\]#', '<span class="match\\1">', $highlighted);
    $highlighted = preg_replace('#\[/([0-9]+)\]#', '</span>', $highlighted);

    return $highlighted;
}

:hakraがチャットで私に指摘したように、正規表現のサブグループが1つの全体的な一致内で複数回発生する可能性がある場合(たとえば、'/ a(b | c)+ /')、preg_match_all最後の情報のみが通知されますそれらの一致の-あなたが期待/望むかもしれないようhighlight_regex_matches('/a(b|c)+/', 'abc')に戻っていないので。ただし、それ以外のすべての一致するグループは引き続き正しく機能するため、正規表現がどのように一致したかを示すかなり良い指標となります。'[0]ab[1]c[/1][/0]''[0]a[1]b[/1][1]c[/1][/0]'highlight_regex_matches('/a((b|c)+)/', 'abc')'[0]a[1]b[2]c[/2][/1][/0]'

于 2012-08-13T19:38:05.177 に答える
0

最初の答えの下であなたのコメントを読んで、私はあなたがあなたが意図したように実際に質問を定式化していないと確信しています。ただし、具体的に求めていることに従うと、次のようになります。

$pattern = "/(.)/";
$subject = "abc";

$callback = function($matches) {
    if ($matches[0] !== $matches[1]) {
        throw new InvalidArgumentException(
            sprintf('you do not match thee requirements, go away: %s'
                    , print_r($matches, 1))
        );
    }
    return sprintf('<span class="match0"><span class="match1">%s</span></span>'
                   , htmlspecialchars($matches[1]));
};
$result = preg_replace_callback($pattern, $callback, $subject);

不平を言う前に、まず問題を説明する際の欠点がどこにあるかを見てください。実際に結果を解析して一致したいと思っているような気がします。ただし、サブマッチを実行したい。正規表現を解析して、使用されているグループを見つけない限り、これは機能しません。これまでのところそうではなく、あなたの質問にもこの回答にも当てはまりません。

したがって、この例は、要件としてパターン全体である必要がある1つのサブグループに対してのみお願いします。それとは別に、これは完全に動的です。

関連している:

于 2012-08-13T18:13:39.650 に答える
0

私はstackoverflowに投稿することにあまり慣れていないので、これを台無しにしないことを望みます。これは@IMSoPとほぼ同じ方法で行いますが、少し異なります。

次のようにタグを保存します。

$tags[ $matched_pos ]['open'][$backref_nr] = "open tag";
$tags[ $matched_pos + $len ]['close'][$backref_nr] = "close tag";

ご覧のとおり、@IMSoPとほぼ同じです。

次に、@ IMSoPのように挿入して並べ替える代わりに、次のように文字列を作成します。

$finalStr = "";
for ($i = 0; $i <= strlen($text); $i++) {
    if (isset($tags[$i])) {
        foreach ($tags[$i] as $tag) {
            foreach ($tag as $span) {
                $finalStr .= $span;
            }
        }
    }
    $finalStr .= $text[$i];
}

$textで使用されているテキストはどこにありますかpreg_match_all()

私のソリューションは@IMSoPよりもわずかに速いと思います。なぜなら、彼は毎回ソートする必要があるからですしかし、私にはわかりません。

今の私の主な心配事はパフォーマンスです。しかし、これよりも速く動作させることは不可能かもしれませんか?

私は再帰的なことを実行しようとしてpreg_replace_callback()きましたが、今のところそれを機能させることができませんでした。preg_replace_callback()は非常に高速のようです。とにかく私が現在していることよりもはるかに速い。

于 2012-08-14T12:51:04.627 に答える
-1

簡単なマッシュアップ、なぜ正規表現を使用するのですか?

$content = "abc";
$endcontent = "";

for($i = 0; $i > strlen($content); $i++)
{
    $endcontent .= "<span class=\"match0\"><span class=\"match1\">" . $content[$i] . "</span></span>";
}

echo $endcontent;
于 2012-08-13T14:36:25.533 に答える