如何正则表达式匹配未知数量的重复元素?
How do I regex-match an unknown number of repeating elements?
我正在尝试编写一个 Ruby 脚本,用它们的 px 等价物替换 CSS 文件中的所有 rem 值。这将是一个示例 CSS 文件:
body{font-size:1.6rem;margin:4rem 7rem;}
我想要获得的 MatchData 是:
# Match 1 Match 2
# 1. font-size 1. margin
# 2. 1.6 2. 4
# 3. 7
但是我完全不知道如何获得多个不同的 MatchData 结果。最接近我的正则表达式是这个 (you can also take a look at it at Rubular):
/([^}{;]+):\s*([0-9.]+?)rem(?=\s*;|\s*})/i
这将匹配值声明的单个实例(因此它将正确地 return 所需的匹配 1 结果),但完全忽略倍数。
我也尝试了一些与 ([0-9.]+?rem\s*)+
类似的方法,但这也没有 return 想要的结果,而且我觉得我走的路不对,因为它不会 return 多个结果数据集。
编辑 在回答了建议之后,我最终解决了这样的问题:
# 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
此正则表达式将为您的示例完成工作:
([^}{;]+):(?:([0-9\.]+?)rem\s?)?(?:([0-9\.]+?)rem\s?)
但是用这个你不能匹配像这样的东西:margin:4rem 7rem 9rem
您无法捕获动态数量的匹配组(至少在 ruby 中不能)。
相反,您可以执行以下任一操作:
- 捕获整个值并在 space
上拆分
- 使用多级匹配首先捕获整个 key/value 对,然后匹配值。您可以在 ruby.
中的 match
方法上使用块
这是我能够做到的:DEMO
正则表达式:(?<={|;)([^:}]+)(?::)([^A-Za-z]+)
这就是我的结果:
# Match 1 Match 2
# 1. font-size 1. margin
# 2. 1.6 2. 4
正如@koffeinfrei 所说,动态捕获在 Ruby 中是不可能的。捕获整个字符串并删除空格会更聪明。
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">
# ]
后者match
可能很容易适应return任何你想要的,value.split(/\s+/)
将return所有值,\d+
而不是.+
将仅匹配数字等
我正在尝试编写一个 Ruby 脚本,用它们的 px 等价物替换 CSS 文件中的所有 rem 值。这将是一个示例 CSS 文件:
body{font-size:1.6rem;margin:4rem 7rem;}
我想要获得的 MatchData 是:
# Match 1 Match 2
# 1. font-size 1. margin
# 2. 1.6 2. 4
# 3. 7
但是我完全不知道如何获得多个不同的 MatchData 结果。最接近我的正则表达式是这个 (you can also take a look at it at Rubular):
/([^}{;]+):\s*([0-9.]+?)rem(?=\s*;|\s*})/i
这将匹配值声明的单个实例(因此它将正确地 return 所需的匹配 1 结果),但完全忽略倍数。
我也尝试了一些与 ([0-9.]+?rem\s*)+
类似的方法,但这也没有 return 想要的结果,而且我觉得我走的路不对,因为它不会 return 多个结果数据集。
编辑 在回答了建议之后,我最终解决了这样的问题:
# 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
此正则表达式将为您的示例完成工作:
([^}{;]+):(?:([0-9\.]+?)rem\s?)?(?:([0-9\.]+?)rem\s?)
但是用这个你不能匹配像这样的东西:margin:4rem 7rem 9rem
您无法捕获动态数量的匹配组(至少在 ruby 中不能)。
相反,您可以执行以下任一操作:
- 捕获整个值并在 space 上拆分
- 使用多级匹配首先捕获整个 key/value 对,然后匹配值。您可以在 ruby. 中的
match
方法上使用块
这是我能够做到的:DEMO
正则表达式:(?<={|;)([^:}]+)(?::)([^A-Za-z]+)
这就是我的结果:
# Match 1 Match 2
# 1. font-size 1. margin
# 2. 1.6 2. 4
正如@koffeinfrei 所说,动态捕获在 Ruby 中是不可能的。捕获整个字符串并删除空格会更聪明。
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">
# ]
后者match
可能很容易适应return任何你想要的,value.split(/\s+/)
将return所有值,\d+
而不是.+
将仅匹配数字等