Positive Lookahead in CSS

positive lookahead in css

You cannot yet declare which part of the selector is the subject. The subject is always the last element of the selector in CSS, until we get the power to move that, likely using the $ or ! syntax.

// Always selects the .specialClass div which follows another div
div + div.specialClass {
color: red;
}

In the future, you'll be able to make the first div the subject, likely with the following syntax:

// Selects any `div` preceding any `div.specialClass`
$div + div.specialClass { // or maybe div! + div.specialClass
color: red;
}

Your only workaround in the interim is to use JavaScript. A tool like jQuery would make this very trivial:

$("div + div.specialClass").prev();

Which is now a reference to all div elements immediately preceding any div.specialClass.

Demo: http://jsfiddle.net/jonathansampson/HLfCr/

Source: http://dev.w3.org/csswg/selectors4/#subject

Regex negative lookbehind and lookahead: equivalence and performance

Is it going to have an impact on performances?

In most cases, the more steps a regex needs to find a match, the slower the performance is. Although it also depends what platform you will use the regex in later (say, if you test a regex for use in .NET using regex101.com, it does not mean it will cause a catastrophic backtracking with a lazy dot matching regex failing with a long text).

Are the two regex really functionally equivalent?

No, they aren't. (?<!\.png|\.css)$ finds an end of the line that is not preceded with .png or .css. ^(?!.*[.]png|.*[.]css$).*$ finds lines that do not contain .png or the lines that do not end with .css. To make them "equivalent" (that is, if you want to make sure the lines ending with .png or .css are not matched), use

^(?!.*[.](?:png|css)$).*$
^^^^^^^^^^^^

Make sure the $ is checked after both png and css in the negative lookahead.

There will still be the difference between the regexps: the first will just match the end of the line, and the second will match the whole line.

Is there a way to speed up the lookbehind solution?

Note that the lookbehind in Pattern 1 is checked at each location inside the string. The lookahead in Pattern 2 is only checked once, at the very beginning of the string. That is why an anchored lookahead solution will be faster UNDER one condition - if you cannot use a RightToLeft modifier that is only available in few regex flavors (e.g. .NET).

The $(?<!\.(?:png|css)$) lookbehind solution is faster than Pattern 1 because the lookbehind pattern is checked just once, after reaching the end of string/line. Still, this takes a bit more steps because of the implementation of a lookbehind that is costlier than a lookahead.

To really find out which solution is fastest, you need to set up performance tests in your environment.

Regex positive lookbehind alternative for word wrap

Use

return text.replace(/(.{45})(?!$)/g, "$1\r\n");

See regex proof.

EXPLANATION

NODE                     EXPLANATION
--------------------------------------------------------------------------------
( group and capture to \1:
--------------------------------------------------------------------------------
.{45} any character except \n (45 times)
--------------------------------------------------------------------------------
) end of \1
--------------------------------------------------------------------------------
(?! look ahead to see if there is not:
--------------------------------------------------------------------------------
$ before an optional \n, and the end of
the string
--------------------------------------------------------------------------------
) end of look-ahead

regex replace to match url() paths in css and replace with asset_path

I have conjured a beast of a regex that I believe does the job. It requires a positive lookbehind and a positive lookahead which I don't know if Grunt supports since I don't know what that is. I'm going to assume it does, otherwise I don't think it's possible without the lookaround.

I tested this regex using C#, so here it is!

Regex:

(?<=url\s*\('([\w\._-]+/)*)([\w\._-]+)(?='\))

Test String:

url ('fruit-veggie/apple_orange-pie/this.is-my_file.png')

I will break this down as it befuzzles even me. This is composed of 3 major parts.

Positive lookbehind:

(?<=url\s*\('([\w\._-]+/)*)
  1. The (?<=) indicates whatever comes between the = and ) has to be part of the pattern that follows, but it will not be part of the match.
  2. url\s*\(' will match url (' with or without spaces.
  3. ([\w\._-]+/)* will match any string that contains at least one word character, dot, underscore, or dash, followed by a forward slash. This will consume one folder path. The * at the end will make it consume any number of folders because you might not have a folder to begin with.

Actual file name:

([\w\._-]+)

This is identical to the folder pattern except without the forward slash at the end. This will match files without extensions.

Positive lookahead:

(?='\))
  1. (?=) is the same as the positive lookbehind, except this is a lookahead which will check what comes after the pattern that precedes it.

  2. '\) simply checks that the entire string is followed by a quote and a closing bracket.

For the folder/file name pattern, you will have to tweak it based on what characters would be valid in them. So if for whatever crazy reason they can contain a #, you will have to modify those portions of the regex to include that character.

Hopefully Grunt supports this regex, otherwise I will have wasted my time. But this was a fun challenge regardless!

Update

It seems JavaScript doesn't support lookbehinds. If what you're doing is specific to your current project only, why don't you try using two regex instead of one?

function GetFile (s) {
s = s.replace (/url\s*\('([\w\._-]+\/)*/g, '');
return s.match (/[\w\._-]+(?='\))/)[0];
}

var s = "url ('fruit-veggie/apple_orange-pie/this.is-my_file.png')";
console.log (GetFile (s));

This will erase everything up to but not including the first character of the file name. Then it returns the file name without the end quote and bracket, because JavaScript supports lookaheads.

What are non-word boundary in regex (\B), compared to word-boundary?

A word boundary (\b) is a zero width match that can match:

  • Between a word character (\w) and a non-word character (\W) or
  • Between a word character and the start or end of the string.

In Javascript the definition of \w is [A-Za-z0-9_] and \W is anything else.

The negated version of \b, written \B, is a zero width match where the above does not hold. Therefore it can match:

  • Between two word characters.
  • Between two non-word characters.
  • Between a non-word character and the start or end of the string.
  • The empty string.

For example if the string is "Hello, world!" then \b matches in the following places:

 H e l l o ,   w o r l d !
^ ^ ^ ^

And \B matches those places where \b doesn't match:

 H e l l o ,   w o r l d !
^ ^ ^ ^ ^ ^ ^ ^ ^ ^

Positive lookahead + overlapping matches regex

I only tried this on regex101 (marked golang regex), but it seems that it works as expected:

%[0-9a-fA-F][0-9a-fA-F]|(%)

or simpler:

%[0-9a-fA-F]{2}|(%)

Range vs character in Negative lookbehind in regex

Lets first take a look at what your regex does:

  • (?!a) check that the next character is not an a
  • \d* match any amount of digits
  • (?=c) check that the next character is a c

In this one, the (?!a) is rather pointless, as the next character is a digit or c according to the rest of the pattern. (e.g. it will also split b1c into b and c) You might have been looking for a lookbehind (not supported in JS) (?<=a) to check that the previous character was an a.

According to your comment you seem to be confusing negative lookahead (?!pattern) and (positive) lookbehind (?<=pattern)

The second regex is quite similar, except that you check for generic (lowercase) letters instead of certain ones. If there is no digit in between those letters (ac), one can simplify your regex to (?![a-z])(?=[a-z])

  • (?![a-z]) check that the next character is no letter
  • (?=[a-z]) check that the next character is a letter

This can never be true.

Optional whitespaces inside lookbehind regex

Do not use lookbehinds when you can do it easily with a capturing group.

In your case you can do something like:

(\bfont-size:\s*)([0-9]+)

Then use the capturing groups $1 and $2 as you need.

Notepad++ Regex Find/Replace Using Look Behind not Working

Based on comments to the original question here are some work-arounds that solved my problem.

Option #1

Replace the lookbehind portion of the regex statement (?<=[\s,;]) with a simple non-lookbehind group matching statement such as ([\s,;]). This will continue to limit search results to strings beginning with the specified characters in the lookbehind. The only caveat is that in my replacement string e.g. $1 $2 I would need to leave out the undesired matched characters that should not be a part of the replacement string.

Option #2

Use the "Replace All" button. It will perform replacements correctly when using a positive lookbehind in your regex statement as opposed to using the "Replace" button for single replacement.

I went with Options #1 only because it achieves what I need while allowing me to still perform a single replacement at a time. With larger documents I don't want to use "Replace All" until I have thoroughly tested my regex expression.

Firefox bug with regex positive lookaheads?

I'm guessing that maybe you might want to write a simple expression that'd be somewhat similar to:

\s*(<[^>]*>)\s*
Demo 1

or

\s{0,1}(<[^>]*>)\s{0,1}
Demo 2

and replace it with $1.

const regex = /\s{0,1}(<[^>]*>)\s{0,1}/g;const str = `this is <some> test`;const subst = `$1`;
const result = str.replace(regex, subst);
console.log(result);


Related Topics



Leave a reply



Submit