Osm*_*Osm 4 regex google-sheets re2 google-sheets-formula
我在谷歌表格中有一个输入列表,
| 输入 | 所需输出 | “仅演示而不是输入”重复的字母 |
|---|---|---|
| 户外活动 | 匹配 | 哦 |
| 狗 | 没有匹配 | |
| 步 | 没有匹配 | |
| 蜜蜂 | 匹配 | e |
| 棋盘 | 匹配 | s |
| 食谱 | 匹配 | 好的 |
如何在不拆分字符串的情况下验证字符串中的所有字母是否唯一?
换句话说,如果字符串中有一个或多个字母出现两次或多次,则返回TRUE
到目前为止我的流程
除了分割字符串并将字符串的长度除以字符串的唯一字母之外,我还尝试了此解决方案,if = 1“匹配”,否则“不匹配”COUNTA
或者使用正则表达式
,我发现了一种方法来匹配字符串中出现的字母 2 次此演示,REGEXEXTRACT 但等待需要的是当字母在字符串中不唯一时得到TRUE
=REGEXEXTRACT(A1,"o{2}?")
Run Code Online (Sandbox Code Playgroud)
返回:
oo
Run Code Online (Sandbox Code Playgroud)
像这样的事情会做
=REGEXMATCH(Input,"(anyletter){2}?")
Run Code Online (Sandbox Code Playgroud)
或者像这样
=REGEXMATCH(lower(A6),"[a-zA-Z]{2}?")
Run Code Online (Sandbox Code Playgroud)
笔记
TRUEorFALSE代替Matchor 就可以了No Match,保持简单。更多示例
| 输入 | 所需输出 |
|---|---|
| 专业地 | 匹配 |
| 吸引力 | 匹配 |
| 无法控制地 | 匹配 |
| 声名狼藉地 | 没有匹配 |
| 推荐 | 匹配 |
| 审讯 | 匹配 |
| 攻击性 | 匹配 |
| 双重思想 | 没有匹配 |
您使用单个正则表达式明确要求答案。不幸的是,没有使用 RE2 对以前的捕获组进行反向引用之类的东西。因此,如果您详细说明问题的答案,它将如下所示:
=INDEX(IF(A2:A="","",REGEXMATCH(A2:A,"(?i)(?:a.*a|b.*b|c.*c|d.*d|e.*e|f.*f|g.*g|h.*h|i.*i|j.*j|k.*k|l.*l|m.*m|n.*n|o.*o|p.*p|q.*q|r.*r|s.*s|t.*t|u.*u|v.*v|w.*w|x.*x|y.*y|z.*z)")))
Run Code Online (Sandbox Code Playgroud)
由于您正在寻找不区分大小写的匹配(?i)修饰符,因此将有助于将选项减少到仅 26 个字母表字母。我想上面的内容可以写得更简洁一些,比如:
=INDEX(IF(A2:A="","",REGEXMATCH(A2:A,"(?i)(?:"&TEXTJOIN("|",1,REPLACE(REPT(CHAR(SEQUENCE(26,1,65)),2),2,0,".*"))&")")))
Run Code Online (Sandbox Code Playgroud)
编辑1:
使用除上述之外的单个正则表达式执行此操作的唯一其他合理方法(直到我了解@DoubleUnary中的 PREG 支持的 matches 子句语法QUERY())是在 GAS 中创建您自己的 UDF(AFAIK)。它将基于 JavaScript,因此支持反向引用。GAS 不是我的强项,但一个简单的例子可以是:
function REGEXMATCH_JS(s) {
if (s.map) {
return s.map(REGEXMATCH_JS);
} else {
return /([a-z]).*?\1/gi.test(s);
}
}
Run Code Online (Sandbox Code Playgroud)
该模式的([a-z]).*?\1含义是:
([a-z])- 捕获 az 范围内的单个字符;.*?\1- 通过反向引用查找 0+(惰性)字符,直至第一个捕获的字符的副本。匹配是全局的并且不区分大小写。您现在可以致电:
=INDEX(IF(A2:A="","",REGEXMATCH_JS(A2:A)))
Run Code Online (Sandbox Code Playgroud)
编辑2:
对于那些对速度进行基准测试的人,我自己不会对此进行测试,但也许这会加快速度:
=INDEX(REGEXMATCH(A2:INDEX(A:A,COUNTA(A:A)),"(?i)(?:a.*a|b.*b|c.*c|d.*d|e.*e|f.*f|g.*g|h.*h|i.*i|j.*j|k.*k|l.*l|m.*m|n.*n|o.*o|p.*p|q.*q|r.*r|s.*s|t.*t|u.*u|v.*v|w.*w|x.*x|y.*y|z.*z)"))
Run Code Online (Sandbox Code Playgroud)
或者:
=INDEX(REGEXMATCH(A2:INDEX(A:A,COUNTA(A:A)),"(?i)(?:"&TEXTJOIN("|",1,REPLACE(REPT(CHAR(SEQUENCE(26,1,65)),2),2,0,".*"))&")"))
Run Code Online (Sandbox Code Playgroud)
或者:
=REGEXMATCH_JS(A2:INDEX(A:A,COUNTA(A:A)))
Run Code Online (Sandbox Code Playgroud)
分别。知道第一行有一个标题。
在这里创造了一个标杆。
NOW()在单击复选框时创建时间戳。NOW()当最后一行被填充并且复选框被选中时,用于创建另一个时间戳。Math.random该样本是根据每个单词 10 个字符创建的随机数据[A-Za-z]。| 公式 | 第1轮 | 第二轮 | 平均 | % 比最好的慢 |
|---|---|---|---|---|
| 样本量 | 10006 | |||
| [re2](a.*a|b.*b)JvDv | 0:00:19 | 0:00:19 | 0:00:19 | -15.15% |
| [re2+递归]MASTERMATCH_RE2 | 0:00:27 | 0:00:24 | 0:00:26 | -54.55% |
| [查找+递归]MASTERMATCH | 0:00:17 | 0:00:16 | 0:00:17 | 0.00% |
| [PREG]双元 | 0:00:57 | 0:00:53 | 0:00:55 | -233.33% |
根据浏览器/设备/移动应用程序和非随机样本数据,这有很大差异。但我发现PREG始终比re2慢
使用递归。
这看起来 比基于正则表达式的方法要快得多。创建一个命名函数:
MASTERMATCH
Run Code Online (Sandbox Code Playgroud)
word
Run Code Online (Sandbox Code Playgroud)
要检查的单词
start
Run Code Online (Sandbox Code Playgroud)
开始于
=IF(
MID(word,start,1)="",
FALSE,
IF(
ISERROR(FIND(MID(word,start,1),word,start+1)),
MASTERMATCH(word,start+1),
TRUE
)
)
Run Code Online (Sandbox Code Playgroud)
=ARRAYFORMULA(MASTERMATCH(A2:INDEX(A2:A,COUNTA(A2:A)),1))
Run Code Online (Sandbox Code Playgroud)
或者不区分大小写
=ARRAYFORMULA(MASTERMATCH(lower(A2:A),1))
Run Code Online (Sandbox Code Playgroud)
它使用递归遍历每个字符MID,并使用检查该位置之后是否有相同的字符可用FIND。如果是,则返回true并且不再检查。如果没有,则使用递归继续检查直到最后一个字符。
或者使用regex,创建一个命名函数:
MASTERMATCH_RE2
Run Code Online (Sandbox Code Playgroud)
word
Run Code Online (Sandbox Code Playgroud)
要检查的单词
start
Run Code Online (Sandbox Code Playgroud)
开始于
IF(
MID(word,start,1)="",
FALSE,
IF(
REGEXMATCH(word,MID(word, start, 1)&"(?i).*"&MID(word,start,1)),
TRUE,
MASTERMATCH_RE2(word,start+1)
)
)
Run Code Online (Sandbox Code Playgroud)
=ARRAYFORMULA(MASTERMATCH_RE2(A2:A,1))
Run Code Online (Sandbox Code Playgroud)
或者
=ARRAYFORMULA(MASTERMATCH_RE2(lower(A2:A),1))
Run Code Online (Sandbox Code Playgroud)
它递归遍历每个字符并为该字符创建一个正则表达式。它不是a.*a, b.*b,... ,而是采用第一个字符(使用MID),例如:oinoutdoor并创建一个正则表达式o.*o。如果 regex 对于该正则表达式为正(使用REGEXMATCH),则返回 true 并且不检查其他字母或创建其他正则表达式。
使用lambda,但它很有效。MAP使用和循环遍历每一行和每个字符REDUCE。REPLACE找出单词中每个字符的长度差异。如果大于1,则不再检查长度并返回Match
=MAP(
A2:INDEX(A2:A,COUNTA(A2:A)),
LAMBDA(_,
REDUCE(
"No Match",
SEQUENCE(LEN(_)),
LAMBDA(a,c,
IF(a="Match",a,
IF(
LEN(_)-LEN(
REGEXREPLACE(_,"(?i)"&MID(_,c,1),)
)>1,
"Match",a
)
)
)
)
)
)
Run Code Online (Sandbox Code Playgroud)
如果您确实遇到 lambda 限制,请删除MAP并拖动填充REDUCE公式。
=REDUCE("No Match",SEQUENCE(LEN(A2)),LAMBDA(a,c,IF(a="Match",a,IF(LEN(A2)-LEN(REGEXREPLACE(A2, "(?i)"&MID(A2,c,1),))>1,"Match",a))))
Run Code Online (Sandbox Code Playgroud)
后者也是条件格式的首选。
正如 Daniel Cruz 所说,Google Sheets 具有 、 等功能regexmatch(),regexextract()并regexreplace()使用不支持反向引用的RE2正则表达式。但是,该query()函数使用支持命名捕获组和反向引用的Perl 兼容正则表达式:
=arrayformula( \n iferror( not( iserror( \n match( \n to_text(A3:A), \n query(lower(unique(A3:A)), "where Col1 matches \'.*?(?<char>.).*?\\k<char>.*\' ", 0), \n 0 \n ) \n ) / (A3:A <> "") ) ) \n)\nRun Code Online (Sandbox Code Playgroud)\n在我使用来自 TheMaster 语料库的 1000 个hetogram、pangram 、带变音字母的单词和 10 个字符的伪随机唯一值的样本大小进行的有限测试中,此 PREG 公式的运行速度大约是 JvdV2 RE2 正则表达式的一半。
\nOsm 的样本包含 50,000 个高度重复的样本值,该公式的运行速度是 JvdV2 的 8 倍。
\nPREG 正则表达式比 RE2 正则表达式慢,但优点是您可以更轻松地检查所有字符是否重复。这使您可以使用包含变音字母、数字和其他非英语字母字符的语料库:
\n| 输入 | 输出 |
|---|---|
| 专业地 | 真的 |
| 声名狼藉地 | 错误的 |
| 算盘 | 真的 |
| \xc3\x89lys\xc3\xa9e | 真的 |
| na\xc3\xafve\xc3\x8f | 真的 |
| m\xc3\xa4\xc3\xa4r\xc3\xa4\xc3\xa4v\xc3\xa4 | 真的 |
| 121 | 真的 |
| 123 | 错误的 |
<char>.您还可以通过替换为<char>[\\w\xc3\xa9\xc3\xa4\xc3\xa5\xc3\xb6]或之类的内容来轻松指定要检查的特定字符<char>[^-;,.\\s\\d]。
| 归档时间: |
|
| 查看次数: |
1088 次 |
| 最近记录: |