preg's 'negative lookbehind' -- broken?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Margaret MacDonald

    #1

    preg's 'negative lookbehind' -- broken?

    I'm trying to write a filter that will ignore text of the form
    '\_foo\_' while filtering text of the form '_foo_'. In other words,
    a backslash is meant to protect against the operation of this
    particular filter.

    To detect the leading backslash once I find the underscore, I've been
    trying to use a 'negative lookbehind' in preg_replace. It seems a
    perfect use for it: if we find an under but it was preceded by a
    backslash, don't filter.

    I can't use a positive filter because I have another filter that
    strips the backslashes, leaving only the underscores. So the order in
    which they run is important.

    Unfortunately, I can't seem to get the lookbehind to work...the filter
    runs whether or not there are backslashes. So either the lookbehind
    is broken or I'm having another problem finding the right number of
    backslashes. As I mentioned in my earlier post, passing
    '\_sometext\_' via a form causes the string to become '\\_sometext\\_ '
    with all 4 backslashes being counted as separate literals--which
    requires 8 backslashes in the filter! Quite unexpected, also
    annoying.

    Anyhow, I've tried 4 and I've tried 8 and neither works, which is why
    I wonder whether it's broken.

    Any insights?

    thanks in advance!
    Margaret
    --
    (To mail me, please change .not.invalid to .net, first.
    Apologies for the inconvenience.)
  • Pedro Graca

    #2
    Re: preg's 'negative lookbehind' -- broken?

    Margaret MacDonald wrote:[color=blue]
    > I'm trying to write a filter that will ignore text of the form
    > '\_foo\_' while filtering text of the form '_foo_'. In other words,
    > a backslash is meant to protect against the operation of this
    > particular filter.[/color]
    ....[color=blue]
    > Anyhow, I've tried 4 and I've tried 8 and neither works, which is why
    > I wonder whether it's broken.[/color]

    This

    ========
    <?php
    $input = 'text _text_ text \_text\_ text';

    $rx = '@(?<!\\\\)_(.* )_@U'; // ungreedy matching
    // ^^^^^^^^^ -- look-behind assertion

    $output = preg_replace($r x, '_filtered_', $input);
    echo $input, ' ==> ', $output, "\n";
    ?>
    --------

    works for me. The output is:

    ========
    text _text_ text \_text\_ text ==> text _filtered_ text \_text\_ text
    --------



    --
    USENET would be a better place if everybody read: | to email me: use |
    http://www.catb.org/~esr/faqs/smart-questions.html | my name in "To:" |
    http://www.netmeister.org/news/learn2quote2.html | header, textonly |
    http://www.expita.com/nomime.html | no attachments. |

    Comment

    • Margaret MacDonald

      #3
      Re: preg's 'negative lookbehind' -- broken?

      I wrote:
      [color=blue]
      >I'm trying to write a filter that will ignore text of the form
      >'\_foo\_' while filtering text of the form '_foo_'. In other words,
      >a backslash is meant to protect against the operation of this
      >particular filter.
      >
      >To detect the leading backslash once I find the underscore, I've been
      >trying to use a 'negative lookbehind' in preg_replace. It seems a
      >perfect use for it: if we find an under but it was preceded by a
      >backslash, don't filter.
      >
      >I can't use a positive filter because I have another filter that
      >strips the backslashes, leaving only the underscores. So the order in
      >which they run is important.
      >
      >Unfortunatel y, I can't seem to get the lookbehind to work...the filter
      >runs whether or not there are backslashes. So either the lookbehind
      >is broken or I'm having another problem finding the right number of
      >backslashes. As I mentioned in my earlier post, passing
      >'\_sometext\ _' via a form causes the string to become '\\_sometext\\_ '
      >with all 4 backslashes being counted as separate literals--which
      >requires 8 backslashes in the filter! Quite unexpected, also
      >annoying.
      >
      >Anyhow, I've tried 4 and I've tried 8 and neither works, which is why
      >I wonder whether it's broken.[/color]

      I'd been trying this syntax:

      preg_replace( '/_(?<!\\\\)test_ (?<!\\\\)/', ....)

      based on my understanding of the example in Lerdorf & Tatroe. My
      mental model was that it would find the underscore, step the counter
      and find the lookbehind, then, to satisfy the lookbehind, rewind the
      counter and see whether it could find a backslash. If it does, then
      the test is false and it continues searching.

      But that didn't work and nothing I could do would make it work.

      Then I tried switching the order:

      preg_replace( '/ (?<!\\\\)_test{ ?<!\\\\)/', ....)

      and that DOES seem to work, though it doesn't seem to make sense in
      terms of how the construct is described (lookbehind, etc) and I don't
      know whether it's working by accident or design.

      So my model for this would be that the so-called 'lookbehind' is
      actually buffering the n chars (in my case 1) before the counter so
      that, when the interpreter finds something under the counter (in my
      case the _ ), the 'lookbehind' buffer already has the extra
      information needed to finish the evaluation--the counter is never
      rewound.

      Does anyone else have any experience that would confirm or refute
      this? I really hate to rely on something that might only be working
      by accident.

      Margaret
      --
      (To mail me, please change .not.invalid to .net, first.
      Apologies for the inconvenience.)

      Comment

      • Margaret MacDonald

        #4
        Re: preg's 'negative lookbehind' -- broken?

        Pedro Graca wrote:
        [color=blue]
        >Margaret MacDonald wrote:[color=green]
        >> I'm trying to write a filter that will ignore text of the form
        >> '\_foo\_' while filtering text of the form '_foo_'. In other words,
        >> a backslash is meant to protect against the operation of this
        >> particular filter.[/color]
        >...[color=green]
        >> Anyhow, I've tried 4 and I've tried 8 and neither works, which is why
        >> I wonder whether it's broken.[/color]
        >
        >This
        >
        >========
        ><?php
        >$input = 'text _text_ text \_text\_ text';
        >
        >$rx = '@(?<!\\\\)_(.* )_@U'; // ungreedy matching
        >// ^^^^^^^^^ -- look-behind assertion
        >
        >$output = preg_replace($r x, '_filtered_', $input);
        >echo $input, ' ==> ', $output, "\n";
        >?>
        >--------
        >
        >works for me. The output is:
        >
        >========
        >text _text_ text \_text\_ text ==> text _filtered_ text \_text\_ text
        >--------[/color]

        ha! Thanks, Pedro. That seems to confirm my suspicions.

        Margaret
        --
        (To mail me, please change .not.invalid to .net, first.
        Apologies for the inconvenience.)

        Comment

        • Pedro Graca

          #5
          Re: preg's 'negative lookbehind' -- broken?

          Margaret MacDonald wrote:[color=blue]
          > That seems to confirm my suspicions.[/color]

          No need to be suspicious about it :)

          It's all documented on the manual:
          PHP is a popular general-purpose scripting language that powers everything from your blog to the most popular websites in the world.



          --
          USENET would be a better place if everybody read: | to email me: use |
          http://www.catb.org/~esr/faqs/smart-questions.html | my name in "To:" |
          http://www.netmeister.org/news/learn2quote2.html | header, textonly |
          http://www.expita.com/nomime.html | no attachments. |

          Comment

          Working...