2

How can I match what's between a . and a { and merge them onto a single line without touching the spacing between them.

This regex (\.|@)[^}]*\{ inculdes the ending { no matter what I try, and I need the (\.|@) part so I can add more selectors in there. Online Regex

Yes, I know I shouldn't be doing this with regex, but I have no other option as of now. So any help will be greatly appreciated.

Edit I'll be using this in preg_replace in php

Raw data

.a {
    // rules
}

.a-b,
.a-b .b, .a-b.s
.x .y, .x {
    // rules
}

@a {
    // rules
}

@k {
    // rules
}

Output

.a {
    // rules
}

.a-b, .a-b .b, .a-b.s, .x .y, .x {
    // rules
}

@a {
    // rules
}

@k {
    // rules
}
2
  • You say you don't want to grab the last { yet your pattern is programmed to include it. Commented Dec 13, 2020 at 15:12
  • That's what I need help with... In the end, it's sufficient if the results are like what's in the output. Commented Dec 13, 2020 at 15:16

2 Answers 2

1

You can use

preg_replace('~(?:\G(?!\A)|^[@.])[^{]*?\K\s+~m', ' ', $text)

See the regex demo. Details:

  • (?:\G(?!\A)|^[@.]) - end of the previous successful match (\G(?!\A)) or (|) start of string (^) and then @ or .
  • [^{]*? - any zero or more (but as few as possible) chars other than {
  • \K - match reset operator that discards all text matched so far in the overall match memory buffer
  • \s+ - any one or more whitespace chars.

Note the m flag that is necessary to make ^ match start of a line, not just start of a whole string.

See the PHP demo:

$text = ".a {\r\n    // rules\r\n}\r\n\r\n.a-b,\r\n.a-b .b, .a-b.s\r\n.x .y, .x {\r\n    // rules\r\n}\r\n\r\n@a {\r\n    // rules\r\n}\r\n\r\n@k {\r\n    // rules\r\n}";
echo preg_replace('~(?:\G(?!\A)|^[@.])[^{]*?\K\s+~m', ' ', $text);
Sign up to request clarification or add additional context in comments.

Comments

0

Update: Stupildy, I missed that this was a PHP question, not a JS one. My answer is JS-based; however it is easily adapted to PHP. PHP has preg_replace_callback for the replacement callback, and the pattern will be the same.


If I understand you right, you want to collapse multi-selector CSS rules and media queries into single lines.

The first thing to point out is that your current pattern isn't safe for all CSS selectors. It won't cover those that start with # or *, for example.

Try this:

let edited = str.replace(/^[\*\.#@:\-\[][\s\S]+?(?=\{)/mg, match => {
    return match.replace(/\n/g, ' ');
});

Demo.

Pattern explanation:

  • Anchor to the start of a line
  • The line must start with ., #, *, @, -, : or [, covering most CSS selectors.
  • Following the selector(s), match whatever comes after, until...
  • }
  • We run each match through a callback, replacing line breaks with spaces.

The m (multi-line) flag tells JS to interpret our anchor (^) as pertaining to any line, not just the first.

The g (global) flag handles all instances, not just the first found.

4 Comments

This looks likes JavaScript... I'm using PHP.
Oh god... that's what I get for not paying attention. Oh well, you can basically adapt the concepts from my answer for PHP. PHP has preg_replace_callback to run the replacement callback, and the pattern should work the same.
I was hoping to be able to do it one single regex. instead of using callbacks etc.
Why? The tools exist to allow you to accomplish tasks. Why ignore them?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.