regex Google Sheets公式中存在多个正则表达式匹配项

5cg8jx4n  于 2022-12-14  发布在  Go
关注(0)|答案(7)|浏览(159)

我尝试使用Google Sheets正则表达式公式来获取给定字符串中连字符前面的所有数字的列表(比如在单元格A1中):

=REGEXEXTRACT(A1, "\d-")

我的问题是它只返回第一个匹配项...*如何获取 * 所有匹配项
范例文字:

"A1-Nutrition;A2-ActPhysiq;A2-BioMeta;A2-Patho-jour;A2-StgMrktg2;H2-Bioth2/EtudeCas;H2-Bioth2/Gemmo;H2-Bioth2/Oligo;H2-Bioth2/Opo;H2-Bioth2/Organo;H3-Endocrino;H3-Génétiq"

我的公式返回1-,而我想得到1-2-2-2-2-2-2-2-2-2-3-3-(作为数组或连接文本)。
我知道我可以使用脚本或其他函数(如SPLIT)来实现所需的结果,但我真正想知道的是如何获得一个re 2正则表达式来返回“REGEX.*“GoogleSheets公式中的多个匹配项。
我也尝试过用REGEXREPLACE删除不需要的文本,但也没有成功(我无法删除连字符之前的其他数字)。
任何帮助都很感激!谢谢:)

oknwwptz

oknwwptz1#

实际上,您可以使用regexreplace在单个公式中完成此操作,用捕获组将所有值括起来,而不是替换文本:

=join("",REGEXEXTRACT(A1,REGEXREPLACE(A1,"(\d-)","($1)")))

基本上,它所做的就是用一个“捕获组”包围\d-的所有示例,然后使用regex extract,它会整洁地返回所有捕获。如果你想将其连接回一个字符串,你可以使用join将其打包回一个单元格:

nxowjjhe

nxowjjhe2#

您可以在脚本编辑器中创建自己的自定义函数:

function ExtractAllRegex(input, pattern,groupId) {
  return [Array.from(input.matchAll(new RegExp(pattern,'g')), x=>x[groupId])];
}

或者,如果需要返回用分隔符连接的单个单元格中的所有匹配项:

function ExtractAllRegex(input, pattern,groupId,separator) {
  return Array.from(input.matchAll(new RegExp(pattern,'g')), x=>x[groupId]).join(separator);
}

那么,就叫它=ExtractAllRegex(A1, "\d-", 0, ", ")吧。

说明

  • input-当前单元格值
  • pattern-正则表达式模式
  • groupId-捕获要提取的组ID
  • separator-用于连接匹配结果的文本。
anauzrmj

anauzrmj3#

编辑

我想出了更一般的解决办法:
=regexreplace(A1,"(.)?(\d-)|(.)","$2")
它只使用第二组匹配项$2替换除第二组匹配项(\d-)之外的任何文本。

"(.)?(\d-)|(.)"
  1    2    3  
  Groups are in ()
  ---------------------------------------
 "$2" -- means return the group number 2

学习正则表达式:https://regexone.com
试试这个公式:
=regexreplace(regexreplace(A1,"[^\-0-9]",""),"(\d-)|(.)","$1")
它将像这样处理字符串:
"A1-Nutrition;A2-ActPhysiq;A2-BioM---eta;A2-PH3-Généti***566*9q"
输出为:
1-2-2-2-3-

gev0vcfq

gev0vcfq4#

我无法获得适用于我的案例的公认答案。我想这样做,但需要一个快速的解决方案,并使用以下方法:
输入:

1111 days, 123 hours 1234 minutes and 121 seconds

预期输出:

1111 123 1234 121

公式:

=split(REGEXREPLACE(C26,"[a-z,]"," ")," ")
t0ybt7op

t0ybt7op5#

这似乎是工作,我已经试图验证它。
逻辑是
(1)将连字符后面的字母替换为空
(2)将后面没有连字符的任何数字替换为空
(3)将数字或勒胡号以外的所有项目取代为空白

=regexreplace(A1,"[a-zA-Z]-|[0-9][^-]|[a-zA-Z;/é]","")

测试结果

1-2-2-2-2-2-2-2-2-2-3-3-

分析

我不得不按程序逐步检查这些选项,以使自己相信这是正确的。根据this reference,当存在由管道符号分隔的替代选项时,正则表达式应该按照从左到右的顺序匹配它们。除非规则1优先,否则上面的公式不能正常工作(否则,在规则(1)生效之前,它会将除数字或连字符之外的所有字符减少为空,并且您会从“Patho-jour”中获得一个额外的连字符)。
这里有一些我认为它必须如何处理文本的例子

p4rjhz4m

p4rjhz4m6#

使用RegexReplace捕获组,然后执行RegexExctract的解决方案在这里也有效,但有一个陷阱
=join(“",正则表达式提取(A1,正则表达式替换(A1,"(\d-)","($1)”)))
如果您试图获取值的单元格包含特殊字符,如括号“(“或问号“?”,则提供的解决方案将不起作用。
在我的例子中,我试图列出单元格中包含的所有“变量文本”。这些“变量文本“是这样写在里面的:“{example_name}"。但是单元格的全部内容都有特殊字符,使得regex公式确实中断。当我删除这些特殊字符时,我就可以像解决方案一样列出所有捕获的组。

flvtvl50

flvtvl507#

有两种通用('Excel' / 'native' /非应用程序脚本)解决方案返回REGEXEXTRACT样式的正则表达式匹配数组
方法1)

在匹配项周围插入分隔符,删除垃圾,并调用SPLIT

正则表达式的工作方式是从左到右迭代字符串,然后“消耗”。如果我们小心地消耗垃圾值,我们可以把它们扔掉。
(This绕过了当前公认的解决方案所面临的问题,即正如卡洛斯Eduardo Oliveira所提到的,如果语料库文本包含特殊的正则表达式字符,则它显然会失败。)
首先,我们选择一个分隔符,这个分隔符必须不存在于文本中。正确的方法是解析文本,用一个“临时分隔符”临时替换我们的分隔符,就像如果我们要使用逗号",",我们首先用"<<QUOTED-COMMA>>"之类的字符替换所有现有的逗号,然后再取消替换它们。但是,为了简单起见,我们将从专用的unicode块中抓取一个随机字符(如),并将其用作特殊的分隔符(注意,它是2个字节...... google电子表格可能不会以一致的方式计算字素中的字节数,但我们稍后会注意)。

=SPLIT(
  LAMBDA(temp,
    MID(temp, 1, LEN(temp)-LEN(""))
  )(
    REGEXREPLACE(
      "xyzSixSpaces:[      ]123ThreeSpaces:[   ]aaaa 12345",".*?(   |$)",
      "$1"
    )
  ),
  ""
)

我们只需要使用lambda来定义temp=“match 1 match 2 match 3 ",然后使用它来移除最后一个分隔符,使其成为“match 1 match 2 match 3”,然后SPLIT它。
取结果的COLUMNS将证明返回了正确的结果,即{" ", " ", " "}
这是一个非常好的函数,可以转换为命名函数,并将其命名为REGEXGLOBALEXTRACT(text,regex)REGEXALLEXTRACT(text,regex),例如:

=SPLIT(
  LAMBDA(temp,
    MID(temp, 1, LEN(temp)-LEN(""))
  )(
    REGEXREPLACE(
      text, 
      ".*?("&regex&"|$)", 
      "$1"
    )
  ),
  ""
)

方法2)

使用递归

使用LAMBDA(也就是让你像其他编程语言一样定义一个函数),你可以使用一些来自lambda演算和函数编程的技巧:定义一个递归函数是一件令人困惑的事情,因为它没有一个简单的方法来引用它自己,所以你必须使用一个技巧/约定:
递归函数技巧:* 要真正定义一个需要引用本身的函数f,请改为定义一个接受参数itself并返回实际所需函数的函数 *;把这个“约定”传递给Y组合子,把它变成一个实际的递归函数
这种函数的管道称为Y-组合子。如果你有一些编程背景,这里有一个good article to understand it
例如,要得到5!的结果(5阶乘,即实现我们自己的FACT(5)),我们可以定义:
命名函数Y(f) = LAMBDA(f, (LAMBDA(x,x(x)))( LAMBDA(x, f(LAMBDA(y, x(x)(y)))) ) )(这是Y-组合子,很神奇;您不必了解它就可以使用它)
命名函数MY_FACTORIAL(n) =

Y(LAMBDA(self,

  LAMBDA(n, 
    IF(n=0, 1, n*self(n-1))
  )

))

MY_FACTORIAL(5)结果:120
Y-组合子使得编写递归函数看起来相对容易,就像是编程类的入门。我使用命名函数是为了清晰起见,但你可以以牺牲理智为代价将它们全部丢弃...

=LAMBDA(Y,
  Y(LAMBDA(self, LAMBDA(n, IF(n=0,1,n*self(n-1))) ))(5)
)(
  LAMBDA(f, (LAMBDA(x,x(x)))( LAMBDA(x, f(LAMBDA(y, x(x)(y)))) ) )
)

这是如何应用到手头的问题?好的递归解决方案如下:
在下面的伪代码中,我使用'function'代替LAMBDA,但它们是一样的:

// code to get around the fact that you can't have 0-length arrays
function emptyList() {
  return {"ignore this value"}
}
function listToArray(myList) {
  return OFFSET(myList,0,1)
}

function allMatches(text, regex) {
  allMatchesHelper(emptyList(), text, regex)
}

function allMatchesHelper(resultsToReturn, text, regex) {
  currentMatch = REGEXEXTRACT(...)
  if (currentMatch succeeds) {
    textWithoutMatch = SUBSTITUTE(text, currentMatch, "", 1)
    return allMatches(
      {resultsToReturn,currentMatch}, 
      textWithoutMatch, 
      regex
    )
  } else {
    return listToArray(resultsToReturn)
  }
}

不幸的是,递归方法是二次增长(因为它会一遍又一遍地将结果追加到自身,同时用越来越小的字节重新创建巨大的搜索字符串,所以1+2+3+4+5+...= big^2,这可能会增加很多时间),所以如果你有很多匹配,速度可能会很慢。为了提高速度,最好留在正则表达式引擎中,因为它可能是高度优化的。
当然,如果你想在表达式中使用varName,你可以通过与LAMBDA(varName, expr)(varValue)进行临时绑定来避免使用命名函数。(你可以将这种模式定义为命名函数=cont(varValue),以反转参数的顺序,使代码更简洁,或者不这样做。)

  • 每当我使用varName = varValue时,请改为这样写。
  • 要查看匹配是否成功,请使用ISNA(...)

它看起来像这样:
命名函数allMatches(resultsToReturn, text, regex)未测试

LAMBDA(helper,
  OFFSET(
    helper({"ignore"}, text, regex),
  0,1)
)(

  Y(LAMBDA(helperItself,

  LAMBDA(results, partialText,
    LAMBDA(currentMatch,
      IF(ISNA(currentMatch),
        results,
        LAMBDA(textWithoutMatch,
          helperItself({results,currentMatch}, textWithoutMatch)
        )(
          SUBSTITUTE(partialText, currentMatch, "", 1)
        )
      )
    )(
      REGEXEXTRACT(partialText, regex)
    )
  )

  ))
)

相关问题