0

I'm trying to write a Ruby script that replaces all rem values in a CSS file with their px equivalents. This would be an example CSS file:

body{font-size:1.6rem;margin:4rem 7rem;}

The MatchData I'd like to get would be:

#    Match 1           Match 2
#    1. font-size      1. margin
#    2. 1.6            2. 4
#                      3. 7

However I'm entirely clueless as to how to get multiple and different MatchData results. The RegEx that got me closest is this (you can also take a look at it at Rubular):

/([^}{;]+):\s*([0-9.]+?)rem(?=\s*;|\s*})/i

This will match single instances of value declarations (so it will properly return the desired Match 1 result), but entirely disregards multiples.

I also tried something along the lines of ([0-9.]+?rem\s*)+, but that didn't return the desired result either, and doesn't feel like I'm on the right track, as it won't return multiple result data sets.


EDIT After the suggestions in the answers, I ended up solving the problem like this:

# search for any declarations that contain rem unit values and modify blockwise
@output.gsub!(/([^ }{;]+):\s*([^}{;]*[0-9.]rem+[^;]*)(?=\s*;|\s*})/i) do |match|
  # search for any single rem value
  string = match.gsub(/([0-9.]+)rem/i) do |value|
    # convert the rem value to px by multiplying by 10 (this is not universal!)
    value = sprintf('%g', Regexp.last_match[1].to_f * 10).to_s + 'px'
  end
  string += ';' + match # append the original match result to the replacement
  match = string # overwrite the matched result
end
TheThirdMan
  • 1,482
  • 1
  • 14
  • 27

4 Answers4

1

You can't capture a dynamic number of match groups (at least not in ruby).

Instead you could do either one of the following:

  1. Capture the whole value and split on space
  2. Use multilevel matching to capture first the whole key/value pair and secondly match the value. You can use blocks on the match method in ruby.
koffeinfrei
  • 1,985
  • 13
  • 19
  • The second suggestion is great - by modifying the string blockwise, there is no need to assume anything about the string, trying to mess with seperation (as you'd need to not only seperate spaces, but also parentheses and other characters, *then* search for occurrences of rem anyway). Thanks for clearing this up! I added the way I ended up solving my problem to the first post. – TheThirdMan Aug 03 '16 at 11:21
0

This regex will do the job for your example :

([^}{;]+):(?:([0-9\.]+?)rem\s?)?(?:([0-9\.]+?)rem\s?)

But whith this you can't match something like : margin:4rem 7rem 9rem

baddger964
  • 1,199
  • 9
  • 18
  • While this is the only way I've come accross so far if extended to 4 properties (which is the most I can imagine to occur in CSS), I was specifically wondering whether it's possible to match an unknown amount of elements. Your answer unfortunately also creates an empty second result for the first match, which would lead to rather unnecessary post-processing; see [Rubular](http://rubular.com/r/rL1x3VQNmp) – TheThirdMan Aug 03 '16 at 08:22
  • 1
    You can't match an unknown amount of element, read this for explanation : http://stackoverflow.com/questions/464736/python-regular-expressions-how-to-capture-multiple-groups-from-a-wildcard-expr#464755 – baddger964 Aug 03 '16 at 08:27
0

This is what I've been able to do: DEMO

Regex: (?<={|;)([^:}]+)(?::)([^A-Za-z]+)

And this is what my result looks like:

 #    Match 1           Match 2
 #    1. font-size      1. margin
 #    2. 1.6            2. 4

As @koffeinfrei says, dynamic capture isn't possible in Ruby. Would be smarter to capture the whole string and remove spaces.

Anshul Rai
  • 772
  • 7
  • 21
0
str = 'body{font-size:1.6rem;margin:4rem 7rem;}'
str.scan(/(?<=[{; ]).+?(?=[;}])/)
   .map { |e| e.match /(?<prop>.+):(?<value>.+)/ }
#⇒ [
#    [0] #<MatchData "font-size:1.6rem" prop:"font-size" value:"1.6rem">,
#    [1] #<MatchData "margin:4rem 7rem" prop:"margin" value:"4rem 7rem">
# ]

The latter match might be easily adapted to return whatever you want, value.split(/\s+/) will return all the values, \d+ instead of .+ will match digits only etc.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160