草庐IT

php - 通过在@font-face 中搜索替换从@font-face 中提取网址

coder 2024-04-22 原文

我有一个网络服务,可以重写 css 文件中的 url,以便可以通过 CDN 提供它们。

css 文件可以包含图像或字体的 url。

我目前有以下正则表达式来匹配 css 文件中的所有 url:

(url\(\s*([\'\"]?+))((?!(https?\:|data\:|\.\.\/|\/))\S+)((\2)\s*\))

但是,我现在想引入对自定义字体的支持,并且需要在 @font-fontface 中定位 url:

@font-face {
  font-family: 'FontAwesome';
  src: url("fonts/fontawesome-webfont.eot?v=4.0.3");
  src: url("fonts/fontawesome-webfont.eot?#iefix&v=4.0.3") format("embedded-opentype"), url("fonts/fontawesome-webfont.woff?v=4.0.3") format("woff"), url("fonts/fontawesome-webfont.ttf?v=4.0.3") format("truetype"), url("fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular") format("svg");
  font-weight: normal;
  font-style: normal;
}

然后我想到了以下内容:

@font-face\s*\{.*(url\(\s*([\'\"]?+))((?!(https?\:|data\:|\.\.\/|\/))\S+)((\2)\s*\))\s*\}

问题是这会匹配所有内容而不仅仅是里面的 url。我想我可以像这样使用 lookbehind:

(?<=@font-face\s*\{.*)(url\(\s*([\'\"]?+))((?!(https?\:|data\:|\.\.\/|\/))\S+)((\2)\s*\))(?<=-\s*\})

不幸的是,PCRE(PHP 使用的)不支持 lookbehind 中的变量重复,所以我被卡住了。

我不希望通过扩展名检查字体,因为某些字体具有 .svg 扩展名,这可能与具有 .svg 扩展名的图像冲突。

此外,我还想修改我原来的正则表达式以匹配所有其他不在 @font-face 中的 url:

.someclass {
  background: url('images/someimage.png') no-repeat;
}

由于我无法使用后视,我如何从 @font-face 和不在 @font-face 中的那些中提取 url ?

最佳答案

Disclaimer : You're maybe off using a library, because it's tougher than you think. I also want to start this answer on how to match URL's that are not within @font-face {}. I also suppose/define that the brackets {} are balanced within @font-face {}.
Note : I'm going to use "~" as delimiters instead of "/", this will releave me from escaping later on in my expressions. Also note that I will be posting online demos from regex101.com, on that site I'll be using the g modifier. You should remove the g modifier and just use preg_match_all().
Let's use some regex Fu !!!

第 1 部分:匹配不在 @font-face 中的 url {}

1.1 匹配@font-face {}

哦,是的,这听起来可能很“奇怪”,但您稍后会注意到原因 :)
我们在这里需要一些递归正则表达式:

@font-face\s*    # Match @font-face and some spaces
(                # Start group 1
   \{            # Match {
   (?:           # A non-capturing group
      [^{}]+     # Match anything except {} one or more times
      |          # Or
      (?1)       # Recurse/rerun the expression of group 1
   )*            # Repeat 0 or more times
   \}            # Match }
)                # End group 1

demo

1.2 转义@font-face {}

我们将在前一个正则表达式之后使用 (*SKIP)(*FAIL),它会跳过它。参见 this answer了解它是如何工作的。

demo

1.3 匹配url()

我们将使用这样的东西:

url\s*\(         # Match url, optionally some whitespaces and then (
\s*              # Match optionally some whitespaces
("|'|)           # It seems that the quotes are optional according to http://www.w3.org/TR/CSS2/syndata.html#uri
(?!["']?(?:https?://|ftp://))  # Put your negative-rules here (do not match url's with http, https or ftp)
(?:[^\\]|\\.)*?  # Match anything except a backslash or backslash and a character zero or more times ungreedy
\2               # Match what was matched in group 2
\s*              # Match optionally some whitespaces
\)               # Match )

请注意,我使用的是 \2,因为我已将其附加到之前的第 1 组正则表达式中。
Here("|')(?:[^\\]|\\.)*?\1 的另一种用法。

demo

1.4 匹配url()里面的值

您可能已经猜到我们需要使用一些 lookaround-fu,问题在于 lookbehind,因为它需要固定长度。我有一个解决方法,我将向您介绍 \K 转义序列。它将比赛的开始重置为 token 列表中的当前位置。 more-info
好吧,让我们将 \K 放在表达式中的某处并使用前瞻,我们最终的正则表达式将是:

@font-face\s*    # Match @font-face and some spaces
(                # Start group 1
   \{            # Match {
   (?:           # A non-capturing group
      [^{}]+     # Match anything except {} one or more times
      |          # Or
      (?1)       # Recurse/rerun the expression of group 1
   )*            # Repeat 0 or more times
   \}            # Match }
)                # End group 1
(*SKIP)(*FAIL)   # Skip it
|                # Or
url\s*\(         # Match url, optionally some whitespaces and then (
\s*              # Match optionally some whitespaces
("|'|)           # It seems that the quotes are optional according to http://www.w3.org/TR/CSS2/syndata.html#uri
\K               # Reset the match
(?!["']?(?:https?://|ftp://))  # Put your negative-rules here (do not match url's with http, https or ftp)
(?:[^\\]|\\.)*?  # Match anything except a backslash or backslash and a character zero or more times ungreedy
(?=              # Lookahead
   \2            # Match what was matched in group 2
   \s*           # Match optionally some whitespaces
   \)            # Match )
)

demo

1.5 在PHP中使用模式

我们需要转义一些东西,比如引号、反斜杠 \\\\ = \,使用正确的函数和正确的修饰符:

$regex = '~
@font-face\s*    # Match @font-face and some spaces
(                # Start group 1
   \{            # Match {
   (?:           # A non-capturing group
      [^{}]+     # Match anything except {} one or more times
      |          # Or
      (?1)       # Recurse/rerun the expression of group 1
   )*            # Repeat 0 or more times
   \}            # Match }
)                # End group 1
(*SKIP)(*FAIL)   # Skip it
|                # Or
url\s*\(         # Match url, optionally some whitespaces and then (
\s*              # Match optionally some whitespaces
("|\'|)          # It seems that the quotes are optional according to http://www.w3.org/TR/CSS2/syndata.html#uri
\K               # Reset the match
(?!["\']?(?:https?://|ftp://))  # Put your negative-rules here (do not match url's with http, https or ftp)
(?:[^\\\\]|\\\\.)*?  # Match anything except a backslash or backslash and a character zero or more times ungreedy
(?=              # Lookahead
   \2            # Match what was matched in group 2
   \s*           # Match optionally some whitespaces
   \)            # Match )
)
~xs';

$input = file_get_contents($css_file);
preg_match_all($regex, $input, $m);
echo '<pre>'. print_r($m[0], true) . '</pre>';

demo

第 2 部分:匹配 @font-face 中的 url {}

2.1 不同的方法

我想在 2 个正则表达式中完成这部分,因为匹配 @font-face {} 中的 URL 会很痛苦,同时还要处理大括号 {} 的状态 在递归正则表达式中。

因为我们已经有了我们需要的部分,我们只需要在一些代码中应用它们:

  1. 匹配所有 @font-face {} 实例
  2. 遍历这些并匹配所有的 url()

2.2 代码化

$results = array(); // Just an empty array;
$fontface_regex = '~
@font-face\s*    # Match @font-face and some spaces
(                # Start group 1
   \{            # Match {
   (?:           # A non-capturing group
      [^{}]+     # Match anything except {} one or more times
      |          # Or
      (?1)       # Recurse/rerun the expression of group 1
   )*            # Repeat 0 or more times
   \}            # Match }
)                # End group 1
~xs';

$url_regex = '~
url\s*\(         # Match url, optionally some whitespaces and then (
\s*              # Match optionally some whitespaces
("|\'|)          # It seems that the quotes are optional according to http://www.w3.org/TR/CSS2/syndata.html#uri
\K               # Reset the match
(?!["\']?(?:https?://|ftp://))  # Put your negative-rules here (do not match url\'s with http, https or ftp)
(?:[^\\\\]|\\\\.)*?  # Match anything except a backslash or backslash and a character zero or more times ungreedy
(?=              # Lookahead
   \1            # Match what was matched in group 2
   \s*           # Match optionally some whitespaces
   \)            # Match )
)
~xs';

$input = file_get_contents($css_file);

preg_match_all($fontface_regex, $input, $fontfaces); // Get all font-face instances
if(isset($fontfaces[0])){ // If there is a match then
    foreach($fontfaces[0] as $fontface){ // Foreach instance
        preg_match_all($url_regex, $fontface, $r); // Let's match the url's
        if(isset($r[0])){ // If there is a hit
            $results[] = $r[0]; // Then add it to the results array
        }
    }
}
echo '<pre>'. print_r($results, true) . '</pre>'; // Show the results

demo

Join the regex chatroom !

关于php - 通过在@font-face 中搜索替换从@font-face 中提取网址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21392684/

有关php - 通过在@font-face 中搜索替换从@font-face 中提取网址的更多相关文章

  1. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  2. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  3. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  4. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  5. ruby - 通过 RVM (OSX Mountain Lion) 安装 Ruby 2.0.0-p247 时遇到问题 - 2

    我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search

  6. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

    我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

  7. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  8. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  9. 通过 MacPorts 的 RubyGems 是个好主意吗? - 2

    从MB升级到新的MBP后,Apple的迁移助手没有移动我的gem。我这次是通过macports安装ruby​​gems,希望在下次升级时避免这种情况。有什么我应该注意的陷阱吗? 最佳答案 如果你想把你的gems安装在你的主目录中(在传输过程中应该复制过来,作为一个附带的好处,会让你以你自己的身份运行geminstall,而不是root),将gemhome:键设置为您在~/.gemrc中的主目录中的路径. 关于通过MacPorts的RubyGems是个好主意吗?,我们在StackOverf

  10. ruby - 通过 RVM 安装 Ruby 1.9.2 永远行不通! - 2

    当我执行>rvminstall1.9.2时一切顺利。然后我做>rvmuse1.9.2也很顺利。但是当涉及到ruby​​-v时..sam@sjones:~$rvminstall1.9.2/home/sam/.rvm/rubies/ruby-1.9.2-p136,thismaytakeawhiledependingonyourcpu(s)...ruby-1.9.2-p136-#fetchingruby-1.9.2-p136-#downloadingruby-1.9.2-p136,thismaytakeawhiledependingonyourconnection...%Total%Rece

随机推荐