如何用引号使正则表达式不饿


how to make regexp not hungry with quotes?

如何让它不饿 - preg_match_all('/"['p{L}'p{Nd}а-яА-ЯёЁ -_'.'+]+"/ui', $outStr, $matches);

你的意思是不贪婪,比如找到最短的匹配而不是最长的匹配?*+?量词默认是贪婪的,并且会尽可能匹配。在它们后面添加一个问号,使它们不贪婪。

preg_match_all('/"['p{L}'p{Nd}а-яА-ЯёЁ -_'.'+]+?"/ui', $outStr, $matches);

贪婪的匹配:

"foo" and "bar"
^^^^^^^^^^^^^^^

非贪婪匹配:

"foo" and "bar"
^^^^^

参见:http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php

U (PCRE_UNGREEDY)

这个修饰符颠倒了量词的"贪婪",所以 他们默认不贪婪, 但如果跟着就变得贪婪了?。它 与 Perl 不兼容。它可以 也可以由 (?U) 修饰符设置 在模式内或通过问题 在量词后面标记(例如 .*?

建议

/"['p{L}'p{Nd}а-яА-ЯёЁ -_'.'+]+"/ui

我提交的相当于:

/"['pL'p{Nd}а-яА-ЯёЁ -_.+]+"/ui

向人们展示您正在使用的非 ASCII,以防它不明显,使用'x{⋯}转义,即:

/"['pL'p{Nd}'x{430}-'x{44F}'x{410}-'x{42F}'x{451}'x{401} -_.+]+"/ui

使用命名字符是:

/"['pL'p{Nd}'N{CYRILLIC SMALL LETTER A}-'N{CYRILLIC SMALL LETTER YA}'N{CYRILLIC CAPITAL LETTER A}-'N{CYRILLIC CAPITAL LETTER YA}'N{CYRILLIC SMALL LETTER IO}'N{CYRILLIC CAPITAL LETTER IO} -_.+]+"/ui

顺便说一句,这些是通过 uniquote 脚本运行它们来生成的,第一个使用 uniquote -x 第二个使用 uniquote -v .

是的,我知道或至少相信PHP还不支持命名字符,但它使谈论起来更容易。 此外,它确保他们不会混淆相似之处:

U+0410 ‹А› 'N{CYRILLIC CAPITAL LETTER A}
U+0430 ‹а› 'N{CYRILLIC SMALL LETTER A}
U+0401 ‹Ё› 'N{CYRILLIC CAPITAL LETTER IO}
U+0451 ‹ё› 'N{CYRILLIC SMALL LETTER IO}

为:

U+0041 ‹A› 'N{LATIN CAPITAL LETTER A}
U+0061 ‹a› 'N{LATIN SMALL LETTER A}
U+00CB ‹Ë› 'N{LATIN CAPITAL LETTER E WITH DIAERESIS}
U+00EB ‹ë› 'N{LATIN SMALL LETTER E WITH DIAERESIS}

现在我想起来了,这些都是字母,所以我不明白你为什么要列举西里尔字母列表。这是因为您不想要所有西里尔字母,而只想要那组特定的字母吗? 否则我只会做:

/"['pL'p{Nd} -_.+]+"/ui

在这一点上,我想知道那个/i. 我看不出它的目的是什么,所以只会写:

/"['pL'p{Nd} -_.+]+"/u

如前所述,将最大量化+交换为其相应的最小版本 +? 将起作用:

/"['pL'p{Nd} -_.+]+?"/u

但是,我担心的是[ -_]的范围,即'p{SPACE}-'p{LOW LINE}.我发现这是一个非常奇特的范围。 这意味着这些中的任何一个

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[']^_

首先,您再次包含大写的ASCII字母。 另一方面,您省略了一些符号和标点符号:

% unichars -g ''p{ASCII}' '['pS'pP]' 'ord() < ord(" ") || ord() > ord("_")'
 `  U+0060 GC=Sk GRAVE ACCENT
 {  U+007B GC=Ps LEFT CURLY BRACKET
 |  U+007C GC=Sm VERTICAL LINE
 }  U+007D GC=Pe RIGHT CURLY BRACKET
 ~  U+007E GC=Sm TILDE

(该输出来自 unichars 脚本,以防您好奇。

这似乎很武断。 所以我想知道这对你来说是否还不够好:

/"['pL'p{Nd}'s'pS'pP]+?"/u

现在我想起来了,这两个可能会导致其他问题:

U+0401 ‹Ё› 'N{CYRILLIC CAPITAL LETTER IO}
U+0451 ‹ё› 'N{CYRILLIC SMALL LETTER IO}

这假设它们是NFC形式(由规范分解的规范组合形成)。如果您有可能正在处理尚未规范化为 NFC 形式的数据,那么您将不得不考虑

NFD("'N{CYRILLIC CAPITAL LETTER IO}") => "'N{CYRILLIC SMALL LETTER IE}'N{COMBINING DIAERESIS}"
NFD("'N{CYRILLIC SMALL LETTER IO}")   => "'N{CYRILLIC CAPITAL LETTER IE}'N{COMBINING DIAERESIS}"

现在你有非字母了! 这

% uniprops "COMBINING DIAERESIS"
U+0308 ‹◌̈› 'N{COMBINING DIAERESIS}
    'w 'pM 'p{Mn}
    All Any Assigned InCombiningDiacriticalMarks Case_Ignorable CI Combining_Diacritical_Marks Dia Diacritic M Mn Gr_Ext Grapheme_Extend Graph GrExt ID_Continue IDC Inherited Zinh Mark Nonspacing_Mark Print Qaai Word XID_Continue XIDC

所以也许你真的想要:

/"['pL'pM'p{Nd}'s'pS'pP]+?"/u

如果要将字符串限制为仅包含来自拉丁或西里尔字母的字符(而不是希腊语或片假名),则可以添加该效果的展望:

/"(?:(?=['p{Latin}'p{Cyrillic}])['pL'pM'p{Nd}'s'pS'pP])+?"/u

除了您还需要Common才能获得数字和各种标点符号和符号,以及您需要Inherited才能组合字母后面的标记。 这就引出了我们:

/"(?:(?=['p{Latin}'p{Cyrillic}'p{Common}'p{Inherited}])['pL'pM'p{Nd}'s'pS'pP])+?"/u

现在,这提出了另一种在双引号之间实现最小匹配的方法:

/"(?:(?!")(?=['p{Latin}'p{Cyrillic}'p{Common}'p{Inherited}])['pL'pM'p{Nd}'s'pS'pP])+"/u

不以/x模式运行变得越来越复杂:

/
    "               # literal double quote
    (?:
  ### This group specifies a single char with
  ### three separate constraints:
        # Constraint 1: next char must NOT be a double quote
        (?!")
        # Constraint 2: next char must be from one of these four scripts
        (?=['p{Latin}'p{Cyrillic}'p{Common}'p{Inherited}])
        # Constraint 3: match one of either Letter, Mark, Decimal Number,
        #               whitespace, Symbol, or Punctuation:
        ['pL'pM'p{Nd}'s'pS'pP]
    )       # end constraint group
    +       # repeat entire group 1 or more times
    "       # and finally match another double-quote
/ux

如果是Perl,我会用m{⋯}xu

m{
    "               # literal double quote
    (?:
  ### This group specifies a single char with
  ### three separate constraints:
        # Constraint 1: next char must NOT be a double quote
        (?!")
        # Constraint 2: next char must be from one of these four scripts
        (?=['p{Latin}'p{Cyrillic}'p{Common}'p{Inherited}])
        # Constraint 3: match one of either Letter, Mark, Decimal Number,
        #               whitespace, Symbol, or Punctuation:
        ['pL'pM'p{Nd}'s'pS'pP]
    )       # end constraint group
    +       # repeat entire group 1 or more times
    "       # and finally match another double-quote
}ux

但是我不知道你是否可以像PHP那样做成对的括号分隔符。

希望这有帮助!