gpt4 book ai didi

r - R中的正则表达式:'perl = TRUE'与默认值(PCRE与TRE)

转载 作者:行者123 更新时间:2023-12-03 23:31:39 26 4
gpt4 key购买 nike

当使用像gsubgrep这样的基本R字符串函数时,习惯上总是指定perl = TRUE有什么不利之处吗?这有什么缺点吗?

使用perl=TRUE,表达式可以做更多的事情(例如,您可以使用前瞻性声明或后置声明,或者可以使用\\U进行大小写转换),并且性能也更快,如文档所述。

那么,还有什么缺点吗? perl = TRUE是否不是仅用于向后兼容的默认设置?当perl = TRUE时,我应该注意可移植性问题吗?

最佳答案

比较苹果和橙子不是一个好主意,因为PCRE regex的作用远不如TRE regex烯酸。尽管它们具有相似的结构,但即使如此,外表还是可能是欺骗性的。
TRE和PCRE有何相似之处
TRE支持将文字作为PCRE。文字是普通字符,8位十六进制字符(例如\x1B),宽十六进制字符(例如\x{263a})或转义字符:\a\e\f,< cc>,\n\rPCRE supports more\t(“ control-x”,其中\cx是任何ASCII字符),x(八进制代码\0dd的字符),0dd(八进制代码\ddd的字符或反向引用),ddd(八进制代码为\o{ddd..}的字符),ddd..(十六进制代码为\xhh的字符),hh(十六进制代码为\x{hhh..}的字符)。
两者都有一个hhh.. wildcard,但是在TRE中,它与任何char匹配,在PCRE中,它仅与任何char匹配,但与换行符不符(哪些依赖于换行符PCRE动词.(*CR)(*LF)(*CRLF)(*ANYCRLF))。 (*ANY)将产生gsub(".+", "~", "_\n_"),但是~将产生gsub(".+", "~", "_\n_", perl=TRUE)。另一个相反的示例,要使TRE ~\n~在PCRE中起作用,请使用.修饰符,使用(?n)产生gsub("(?n).+", "~", "_\n_")(无法在行尾样式之间进行选择)。在PCRE模式中,要使~\n~与换行符匹配,您需要在.之前使用(?s)内联DOTALL修饰符(或类似修饰符组的.)。
两者都支持alternation operator,但是由于TRE是text-directed engine最长的替代匹配,而在PCRE中,最左边的替代“获胜”。 (?s:.*)产生sub("(s|su)", "~", "sub")(因为~b是最长的匹配替代方案),但是su产生sub("(s|su)", "~", "sub", perl=TRUE)(因为~ub是匹配的第一个替代方案)。
两者都支持backreferences,但是TRE仅支持多达9个反向引用。如果需要10个或更多,请使用PCRE。 s将找到一个匹配项,没有sub("(.)\\1(.)\\2(.)\\3(.)\\4(.)\\5(.)\\6(.)\\7(.)\\8(.)\\9(.)\\10", "~", "112233445566778899aa", perl=TRUE),则不会检测到任何匹配项。
两者似乎都具有类似character classesperl=TRUE的构造,但实际上,在TRE所属的POSIX世界中,这些被称为方括号表达式。尽管您可以在两个字符范围中都定义文字字符范围,或在它们之间具有OR关系来指定文字字符,但您不能在方括号表达式中使用shorthand char classes,也不能使用任何转义序列。 TRE正则表达式中的[...]模式被视为1个或多个反斜杠或/和[\d]+字母,而在PCRE模式中,它将被解析为1个以上的数字(尝试d(-> gsub("[\\d]+", "~", "00\\99d"))和00~99~(-> gsub("[\\d]+", "~", "00\\99d", perl=TRUE)))。这个事实将解释为什么PCRE模式中的~\~d匹配1+ [\]\-\[]+]-,而不是在需要使用“智能放置”(如[)的TRE表达式中。
TRE和PCRE支持[][-](数字),\d(非数字),\D(“单词”字符),\w(“非单词”字符),\W(任何空格) ,\s(任何非空白)shorthand character classes。但是,PCRE also supports \S(任何垂直空白),\v(除了垂直空白以外的任何字符),\V(任何水平空白),\h(不是水平空白的任何字符), \H(任何非换行符),\N(任何Unicode字素,在处理带变音符号的字母时很有用),\X(任何Unicode换行序列)。
两种口味都支持quantifiers,常规,贪婪\R?*,惰性+??*?,范围/限制量词,例如贪婪+?{3}或< cc>及其懒惰的对象,后面是{8,26}。请注意,TRE对限制量词的支持较差(仅用于supports values lower than 256 for {3,} quantifier,并且对于?和更大的值会抛出“内存不足”异常。请确保始终将{min}值用作最小值)您暗示,因为{2557,} in TRE actually matches 3 occurrences。但是,PCRE支持possessive quantifiers0{,2}++?+。用它们量化的模式不允许回溯到它们,一旦匹配,引擎就不会重试它们此外,就像所有其他基于Henry Spencer的可追溯到1986年的regex库的regex库(Tcl,PostgreSQL)一样,应避免在regex中将懒惰的量词和贪婪的量词混合在同一级别上,因为the first pattern sets the greediness of the whole pattern level并经常导致意外的结果。
两种口味均支持POSIX character classes,可在*+ ... {1,5}+之间使用。但是,TRE支持[(字母数字),](字母),[:alnum:](水平空格),[:alpha:](控制字符),[:blank:](数字),[:cntrl:](可见字符),空格和控制字符),[:digit:](小写字母),[:graph:](所有可打印字符),[:lower:](符号和标点符号),[:print:](任何空格),[:punct:](大写字母)和 (以十六进制值表示的字符)。 PCRE添加 [:space:](“单词”字符)和 [:upper:](任何ASCII字符)。
两者都支持单词边界,但是PCRE模式以更可靠的方式做到这一点。 cf. [:xdigit:]产生 [:word:][:ascii:]产生 gsub("\\b", "~", "CODE")。尽管TRE支持特定的前 ~C~O~D~E~和后 gsub("\\b", "~", "CODE", perl=T)字边界,但PCRE ~CODE~仍然更可靠。
两者都支持内联 modifiers,当在模式中使用它们时,它们会更改某些模式行为,例如 \<。 TRE支持 \>(不区分大小写), \b(点不再与换行符匹配), (?i)(使正则表达式以右关联方式而不是正常的左关联方式进行匹配。默认情况下,串联为左按照Std 1003.1-2001(POSIX) base specifications on regular expressions中给出的语法在TRE中进行关联。更改整个正则表达式匹配的内容)和 i(交换贪婪, n变得贪婪, r变得懒惰)。 PCRE支持 U*?修饰符,以及更多: *iU匹配行的开头/结尾,而不是整个字符串), m(点与换行符匹配), (允许使用空格格式化模式并使用注释), ^(允许使用名称捕获具有相同名称的组), $(如果该组合不是有效的正则表达式标记,则使带有反斜杠的字母转义为错误) ), s(使 x仅匹配字符串的最末尾,否则,它也匹配字符串中最后尾随换行符之前的位置)和 J(仅匹配字符串的开头,就好像前面是 XD)。
回溯方面
请参见 TRE docs:TRE中使用的匹配算法在要搜索的文本长度中使用线性最坏情况时间,并在所用正则表达式的长度中使用二次最坏情况时间。换句话说,该算法的时间复杂度为O(M2N),其中M为正则表达式的长度,N为文本的长度。这会导致类似 $的模式搜索重复的连续子字符串的问题。请参见 Remove repeated elements in a string with R
因此,当您需要大量依赖回溯时,请选择PCRE。
PCRE可以做什么,TRE不能做什么
TRE最明显的缺点是它不支持环视。但是,有 a lot of things that PCRE can boast of

A PCRE verb combination在匹配时匹配和跳过模式
Recursion to match whole patterns that can be nested
Subroutines to match nested, balanced substrings匹配递归结构
\A anchor that matches start of string or the end of the previous successful match
^ branch reset group允许捕获其中的组共享相同的ID
(.{2,})\1+ and opposite (*SKIP)(*FAIL) Unicode character properties
Case-changing operators in the replacement patterns turning the whole or part of the match to lower (\G) or upper case ((?|...|...)) (up to the \p{...} or end of match if it is missing)(实际上,它是R中使用的PCRE库的扩展)
无限宽正向后方替代项 \P{...} match reset operator\L reference
PCRE支持命名捕获组,但是在R. Here is a custom example中并未广泛使用它们。

还有更多内容,例如 anchors\U(字符串的开头), \E(字符串的结尾), \K(字符串的最结尾)), conditional "if-then-else" constructatomic groupings(在与所有格量词相同,但不允许回溯到整个模式序列中,等等。
Windows 7,Linux Ubuntu 16.04,MacOS Sierra 10.12.6中的基准测试
如果要比较R中的TRE和PCRE regex引擎的性能,则应使用简单的模式,将这两个引擎的文字实际匹配。
我主要在Windows中使用R,但是我专门为此测试在Linux VM上安装了R 3.2.3。 MacOS的结果是从 t.kalinowski's answer借用的。
让我们使用微基准库比较TRE(默认)和PCRE( \K)regex性能(请参阅更多的 benchmarking options in R):

library(microbenchmark)

文本为 Wikipedia article about butterflies
txt <- "Butterflies are insects in the macrolepidopteran clade Rhopalocera from the order Lepidoptera, which also includes moths. Adult butterflies have large, often brightly coloured wings, and conspicuous, fluttering flight. The group comprises the large superfamily Papilionoidea, which contains at least one former group, the skippers (formerly the superfamily \"Hesperioidea\") and the most recent analyses suggest it also contains the moth-butterflies (formerly the superfamily \"Hedyloidea\"). Butterfly fossils date to the Paleocene, which was about 56 million years ago."

让我们尝试使用 \A提取括号中的最后一个文本,这是R中非常常见的 \Z操作:
# sub('.*\\((.*)\\).*', '\\1', txt)
# => [1] "formerly the superfamily \"Hedyloidea\""
PCRE_1 <- function(text) { return(sub('.*\\((.*)\\).*', '\\1', txt, perl=TRUE)) }
TRE_1 <- function(text) { return(sub('.*\\((.*)\\).*', '\\1', txt)) }
test <- microbenchmark( PCRE_1(txt), TRE_1(txt), times = 500000 )
test

结果如下:
WINDOWS
-------
Unit: microseconds
expr min lq mean median uq max neval
PCRE_1(txt) 163.607 165.418 168.65393 166.625 167.229 7314.588 5e+05
TRE_1(txt) 70.031 72.446 74.53842 73.050 74.257 38026.680 5e+05

MacOS
-----
Unit: microseconds
expr min lq mean median uq max neval
PCRE_1(txt) 31.693 32.857 37.00757 33.413 35.805 43810.177 5e+05
TRE_1(txt) 46.037 47.199 53.06407 47.807 51.981 7702.869 5e+05

Linux
------
Unit: microseconds
expr min lq mean median uq max neval
PCRE_1(txt) 10.557 11.555 13.78216 12.097 12.662 4301.178 5e+05
TRE_1(txt) 25.875 27.350 31.51925 27.805 28.737 17974.716 5e+05

TRE regex \z仅在Windows中获胜,速度是其两倍以上。在MacOS和Linux上,PCRE( perl=TRUE)版本以相似的比率获胜。
现在,让我们比较不使用大量回溯的正则表达式的性能,并提取双引号中的单词:
# regmatches(txt, gregexpr("\"[A-Za-z]+\"", txt))
# => [1] "\"Hesperioidea\"" "\"Hedyloidea\""
PCRE_2 <- function(text) { return(regmatches(txt, gregexpr("\"[A-Za-z]+\"", txt, perl=TRUE))) }
TRE_2 <- function(text) { return(regmatches(txt, gregexpr("\"[A-Za-z]+\"", txt))) }
test <- microbenchmark( PCRE_2(txt), TRE_2(txt), times = 500000 )
test

WINDOWS
-------
Unit: microseconds
expr min lq mean median uq max neval
PCRE_2(txt) 324.799 330.232 349.0281 332.646 336.269 124404.14 5e+05
TRE_2(txt) 187.755 191.981 204.7663 193.792 196.208 74554.94 5e+05

MacOS
-----
Unit: microseconds
expr min lq mean median uq max neval
PCRE_2(txt) 63.801 68.115 75.51773 69.164 71.219 47686.40 5e+05
TRE_2(txt) 63.825 67.849 75.20246 68.883 70.933 49691.92 5e+05

LINUX
-----
Unit: microseconds
expr min lq mean median uq max neval
PCRE_2(txt) 30.199 34.750 44.05169 36.151 43.403 38428.2 5e+05
TRE_2(txt) 37.752 41.854 52.58230 43.409 51.781 38915.7 5e+05

最佳平均值属于Linux中的PCRE正则表达式,在MacOS中,差异几乎可以忽略,而在Windows中,TRE的运行速度要快得多。
摘要
很明显,TRE(默认)正则表达式库在Windows中的运行速度要快得多。在Linux中,PCRE regex相当快。在MacOS中,PCRE regex仍然是首选,因为使用回溯模式,PCRE regex在该OS中比TRE更快。

关于r - R中的正则表达式:'perl = TRUE'与默认值(PCRE与TRE),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47240375/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com