代码之家  ›  专栏  ›  技术社区  ›  ashleedawg

中间字符串regex/php空白删除在“别处”工作[重复]

  •  0
  • ashleedawg  · 技术社区  · 7 年前

    我有以下错误:

    警告:preg_replace():第38行xxx.php中的未知修饰符']'

    这是第38行的代码:

    <?php echo str_replace("</ul></div>", "", preg_replace("<div[^>]*><ul[^>]*>", "", wp_nav_menu(array('theme_location' => 'nav', 'echo' => false)) )); ?>
    

    有人能帮我解决这个问题吗?

    0 回复  |  直到 7 年前
        1
  •  83
  •   Community Mohan Dere    8 年前

    错误发生的原因

    在php中,正则表达式需要包含在 delimiters . 分隔符可以是任何非字母数字、非反斜杠、非空白字符; / , # , ~ 是最常用的。注意,也可以使用括号样式的分隔符,其中开始和结束括号是开始和结束分隔符,即。 <pattern_goes_here> , [pattern_goes_here] 等等都是有效的。

    “” 未知修饰符x “错误通常发生在以下两种情况:

    • 当正则表达式是 缺少分隔符 .

    • 当你使用 内部分隔符 模式 不逃跑 它。

    在本例中,正则表达式是 <div[^>]*><ul[^>]*> . regex引擎考虑了 < > 作为regex模式,之后的所有内容都作为修饰符。

    Regex: <div[^>  ]*><ul[^>]*>
           │     │  │          │
           └──┬──┘  └────┬─────┘
           pattern    modifiers
    

    ] 这里有一个未知的修饰符,因为它出现在 > 定界符。这就是php抛出这个错误的原因。

    根据模式的不同,未知的修饰语投诉可能是关于 * , + , p , / ) 或几乎任何其他字母/符号。只有 imsxeADSUXJu valid PCRE modifiers .

    如何修复

    解决方法很简单。只需用任何有效的分隔符包装正则表达式模式。在这种情况下,你可以选择 ~ 得到以下信息:

    ~<div[^>]*><ul[^>]*>~
    │                   │
    │                   └─ ending delimiter
    └───────────────────── starting delimiter
    

    如果您在使用分隔符后仍收到此错误,可能是因为模式本身包含所述分隔符的未转义出现。

    或转义分隔符

    /foo[^/]+bar/i 肯定会出错。所以你可以用 \ 如果反斜杠出现在regex中的任何位置:

    /foo[^\/]+bar/i
    │      │     │
    └──────┼─────┴─ actual delimiters
           └─────── escaped slash(/) character
    

    如果regex模式包含如此多的分隔符,那么这是一项乏味的工作。

    当然,更干净的方法是使用不同的分隔符。理想情况下,字符不会出现在regex模式的任何地方,比如 γ - #foo[^/]+bar#i .

    更多阅读:

        2
  •  13
  •   mario    7 年前

    其他例子

    这个 reference answer 已经解释了“未知修饰符”警告的原因。这只是其他典型变体的比较。

    • 忘记添加正则表达式时 / 定界符 / ,第一个非字母符号将假定为一个。因此,警告通常是关于分组后的内容 (…) , […] 元符号:

      preg_match("[a-zA-Z]+:\s*.$"
                  ↑      ↑⬆
      
    • 有时regex已经使用了自定义分隔符( : 但仍包含与未转义文字相同的字符。然后它被误认为是过早的分隔符。这就是为什么下一个符号收到“未知修改器”奖杯的原因:

      preg_match(":\[[\d:/]+\]:"
                  ↑     ⬆     ↑
      
    • 使用经典时 / 分隔符,注意不要在regex中使用它。这在尝试匹配时最常见 unescaped filenames :

      preg_match("/pathname/filename/i"
                  ↑        ⬆         ↑
      

      或匹配角/方括号样式时 tags :

      preg_match("/<%tmpl:id>(.*)</%tmpl:id>/Ui"
                  ↑               ⬆         ↑
      
    • 模板样式(smarty或bbcode)正则表达式模式通常需要 {…} [艾斯] 括号。两者通常都应该逃脱。(最外面的 {} 但两人是例外)。

      他们也被误解为 成对分隔符 不使用实际分隔符时。如果它们还被用作内部的文字字符,那么这当然是一个错误。

      preg_match("{bold[^}]+}"
                  ↑      ⬆  ↑
      
    • 每当警告说 分隔符不能是字母数字或反斜杠 “然后您也完全忘记了分隔符:

      preg_match("ab?c*"
                  ↑
      
    • 未知修饰符“g” “通常表示从javascript或perl逐字复制的regex。

      preg_match("/abc+/g"
                        ⬆
      

      php不使用 /g 全球标志。相反地 preg_replace 函数对所有事件都有效,并且 preg_match_all 是一个事件的“全局”搜索挂件吗 preg_match .

      所以,只要去掉 g 旗帜。

      参见:
      · Warning: preg_replace(): Unknown modifier 'g'
      · preg_replace: bad regex == 'Unknown Modifier'?

    • 一个更特殊的例子是 PCRE U扩展 /x 旗帜 . 这通常(或者应该)用于使regexp更为崇高和可读。

      这允许使用内联 # 评论。php在pcre上实现regex分隔符。但它没有治疗 γ 以任何特殊的方式。这就是 γ 注释可能会变成错误:

      preg_match("/
         ab?c+  # Comment with / slash in between
      /x"
      

      (还值得注意的是, γ 作为 #abc+#x 分隔符可能是双重不可取的。)

    • 在正则表达式中插入变量需要预先转义它们,或者它们本身是有效的正则表达式。你不能事先判断这是否有效:

       preg_match("/id=$var;/"
                   ↑    ↺   ↑
      

      最好申请 $var = preg_quote($var, "/") 在这种情况下。

      参见:
      · Unknown modifier '/' in ...? what is it?

      另一种选择是 \Q…\E 对未引用的文字字符串进行转义:

       preg_match("/id=\Q{$var}\E;/mix");
      

      请注意,这只是元符号的一个方便快捷方式,不可靠/安全。如果那样的话,它会崩溃的 $var 包含文字 '\E' 本身(无论如何不太可能)。确实如此 not mask the delimiter 本身。

    • 不推荐的修饰符/e 是一个完全不同的问题。这与分隔符无关,但隐式表达式解释模式正在逐步淘汰。参见: Replace deprecated preg_replace /e with preg_replace_callback

    可选regex分隔符

    如前所述,解决此错误的最快方法是选择一个不同的分隔符。可以使用任何非字母符号。通常首选视觉上与众不同的:

    • ~abc+~
    • !abc+!
    • @abc+@
    • #abc+#
    • =abc+=
    • %abc+%

    从技术上讲你可以用 $abc$ |abc| 用于分隔符。但是,最好避免使用充当regex元字符本身的符号。

    散列 γ 分隔符也很流行。但要注意的是 x / PCRE_EXTENDED 可读性修改器。你不能用 # inline (?#…) 然后是注释,因为这些将被混淆为分隔符。

    仅引用分隔符

    偶尔你会看到 " ' 用作regex分隔符,其conterpart与php字符串外壳成对使用:

      preg_match("'abc+'"
      preg_match('"abc+"'
    

    就php而言,这是完全有效的。它有时方便而不引人注目,但在ide和编辑器中并不总是清晰可见。

    成对分隔符

    一个有趣的变化是成对的分隔符。不必在正则表达式的两端使用相同的符号,您可以使用任何 <...> (...) [...] {...} 支架/支架组合。

      preg_match("(abc+)"   # just delimiters here, not a capture group
    

    虽然它们中的大多数也用作regex元字符,但您通常可以不费吹灰之力就使用它们。只要正则表达式中的特定大括号/paren正确配对或转义,这些变体就非常可读。

    花哨的regex分隔符

    一个有点懒惰的技巧(这里没有背书)是使用不可打印的ascii字符作为分隔符。通过对regex字符串使用双引号和对分隔符使用八进制转义,这在php中很容易实现:

     preg_match("\001 abc+ \001mix"
    

    这个 \001 只是一个控制角色 艾斯 通常不需要。因此,在大多数regex模式中,它不太可能出现。这使得它适合这里,即使不是很清楚。

    很遗憾你不能使用unicode glyps ❚ 作为分隔符。php只允许使用单字节字符。为什么呢?很高兴你问:

    PCRE上的PHPS分隔符

    这个 preg_* 功能利用 PCRE regex引擎,它本身并不关心或提供分隔符。为了与Perl相似 普雷格* 函数实现它们。这也是为什么你可以使用 modifier letters /ism 而不是仅仅 constants as parameter .

    ext/pcre/php_pcre.c 关于如何预处理regex字符串:

    • 首先,忽略所有前导空格。

    • 任何非字母数字符号都被视为假定的分隔符。注意,php只支持单字节字符:

      delimiter = *p++;
      if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') {
              php_error_docref(NULL,E_WARNING, "Delimiter must not…");
              return NULL;
      }
      
    • 其余的regex字符串从左到右遍历。仅反斜杠 \\ -转义符号将被忽略。 \Q and \E escaping 不受尊重。

    • 如果再次找到分隔符,将验证剩余部分是否仅包含修饰字母。

    • 如果分隔符是 ([{< )]}> )]}> 可对大括号/大括号,则处理逻辑更为详细。

      int brackets = 1;   /* brackets nesting level */
      while (*pp != 0) {
              if (*pp == '\\' && pp[1] != 0) pp++;
              else if (*pp == end_delimiter && --brackets <= 0)
                      break;
              else if (*pp == start_delimiter)
                      brackets++;
              pp++;
      }
      

      它查找正确配对的左右分隔符,但在计数时忽略其他大括号/括号类型。

    • 只有在删除分隔符和修饰符标志之后,原始regex字符串才会传递给pcre后端。

    现在这些都有些无关紧要了。但解释了分隔符警告的来源。整个过程都是为了使perl的兼容性降到最低。当然也有一些小的偏差,比如 [] 字符类上下文在php中未接受特殊处理。

    更多参考资料

        3
  •  0
  •   Danon TylerDurden    7 年前

    如果你想得到一个例外( InvalidPatternException ),而不是警告或使用 preg_last_error() -考虑使用 T-Regx library :

    <?php
    try 
    {
        return pattern('invalid] pattern')->match($s)->all();
    }
    catch (InvalidPatternException $e) 
    {
        // your pattern was invalid
    }