【问题标题】:Regex match $ but not \$ or $$ in Google Apps Script's body.replaceText()正则表达式匹配 $ 但不匹配 Google Apps 脚本 body.replaceText() 中的 \$ 或 $$
【发布时间】:2017-01-31 17:00:29
【问题描述】:

在 Google Apps Script 的 body.replaceText() 中,我想匹配

$Not\$yes $not $$
^         ^

我可以不用反斜杠来匹配美元,用

[^\\]\$

但我不确定如何在文档开始之前和之后处理第二个 $。

值得注意的是,在 body.replaceText() 中,“不完全支持 JavaScript 正则表达式功能的子集,例如捕获组和模式修饰符。” Stack overflow reference

【问题讨论】:

  • 那么我们需要知道你想用你的正则表达式做什么的细节。例如,您是否想在替换中使用它(由您提供的参考暗示)。你想换什么?请提供您正在使用的代码,以便我们为您提供可行的解决方案。
  • 我已经编辑了问题以明确说明body.replaceText()。如果是关于其他事情,那么请edit 明确说明您想要做什么。

标签: javascript regex google-apps-script


【解决方案1】:

Google Apps Script'sbody.replaceText():

body.replaceText() 方法不支持 JavaScript RegExp 的某些部分。众所周知,不支持捕获组以及模式修饰符 (flags)。

您想要替换三字符序列中的第二个字符,其中第一个和第三个字符不是单个明确指定的字符。对于没有前瞻、后瞻断言和/或捕获组的单个正则表达式,这实际上是不可能的。可以匹配两个字符序列[^\$]\$ 并使用负前瞻(?!\$)。但是,由于 Google Apps 脚本不支持捕获组,因此无法仅替换第二个字符 $

因此,我们需要使用另一种方法,该方法涉及进行多次替换,以将文本转换为只能匹配所需内容的形式。我们还必须执行额外的替换以将文本返回到其原始形式,只留下我们想要的操作。

我们想要匹配一个通用的东西(在这种情况下是单个字符 $),但我们想要匹配一些与之非常相似的特定情况(@987654338 @ 和 $$) 将与任何用于匹配所需字符串的正则表达式匹配。我们需要做的是将这些更改为不匹配的内容,执行所需的操作,然后将它们更改回原来的样子。

为此,我们需要执行多个替换操作。下面的代码展示了如何做到这一点。

注意 1:您没有明确指定 $$ 是在文本中的任何位置被忽略,还是仅在文档的开头或结尾处被忽略。 $$$ 呢?目前,下面的代码将“忽略”所有连续包含多个$ 的序列。如果您的要求更具体,可以在此处更改,也可以在自己的代码中更改。

注意 2:对于下面的代码,我假设您只想对要匹配的 $ 字符执行 replaceText()。您实际上并没有说明您想要做什么,但代码可用于适应您想要的任何操作。

//Fake an Object called 'body' so that we can use it as if it was defined
//  in Google Apps Script by using the statement:
//  var body = DocumentApp.getActiveDocument().getBody();
var body = new SomeItem('$Not\\$yes $not $$');

//The following lines were tested as-is in Google Apps Script:
//Open up character sequences which we can be sure are not used in the text.
//  These sequences can be temporarily used to represent the strings we do not want
//  to match.  In this case we make sure that no 'Q' exists which is not followed
//  by a `z`.  This lets us use any Q[^z] sequence for anything we desire.
body.replaceText('Q','Qz'); //Make sure no 'Q' exists that is not 'Qz'
//These look a bit strange because we have to use '\\' to get a single '\' within
//  a string literal.
body.replaceText('\\\\\\$','Qa'); //Use `Qa` to represent `\$`
//Use `QbQb` instead of `Qb` because of an issue with restoring and `$` in replacement
//  string at the end of the string. Also allows handling an odd number of $ in a row.
body.replaceText('\\$\\$','QbQb'); //Use `QbQb` to represent `$$`
body.replaceText('Qb\\$','QbQb'); //Handle an odd number of $ in a row. Not specified
                                  // in the question, but probably desired.
//Here we perform whatever operation was desired on all remaining `$`.
//  In this example we will replace them with '_MATCH_'.
//  However, for the generalized case, we first we have to perform the
//  same preliminary substitution which we did to open up character sequences.
var newText = '_MATCH_';
body.replaceText('\\$',newText.replace(/Q/mg,'Qz')); // Change remaining '$'.
//If the replace() is not performed in the above line then any `Qa`, `Qb`, or `Qz`
//  in the new text will end up being replaced with '\$', '$' and 'Q' respectively.

//Restore the temporary changes
body.replaceText('Qa','\\$');      //Restore `Qa` to `\$`
body.replaceText('Qb','$');        //Restore `Qb` to `$`
body.replaceText('Qz','Q');        //Restore all `Qz` to `Q`
//End of lines to be used in Google Apps Script

console.log(body.text);
<head>
  <script>
//A function which will perform similar to Google Apps Script so that the 
//  code is closer to what is available there. No provision is made to eliminate
//  the capture group feature of RegExp.
function SomeItem(_text){
    this.text = _text;
}
SomeItem.prototype.replaceText=function (regExString,replaceText){
    var theRegExp = new RegExp(regExString,"gm");
    this.text =  this.text.replace(theRegExp,replaceText); 
    //console.log(regExString,replaceText,this.text);
}
  </script>
</head>
<body/>

在 JavaScript 中:

[这部分答案是在将问题更改为希望与不支持 JavaScript RegExp 的全部功能的body.replaceText() 一起使用之前提供的。]

您的RegExp 实际上并不只匹配$。当$ 不在字符串的开头时,它匹配两个字符。 JavaScript 没有后向断言,只有 look-ahead(?=y) 要求 y 跟随匹配,(?!y) 要求 y 不能跟随匹配)。因为 JavaScript 没有后视断言,所以不可能只匹配双字符序列的第二个字符。另一方面,通过使用前瞻断言,我们可以防止基于后续字符的匹配,而无需实际匹配三个字符序列。

为了匹配您想要的内容,我们必须排除 $$ 之前的匹配以及以下字符是 $ 的匹配。这可以通过将$ 添加到不匹配的字符集来完成(除了\;将其更改为[^\\$]),并使用(?!\$) 添加$ 的前瞻排除项.

这导致正则表达式为:

/(^|[^\\$])\$(?!\$)/g

请注意,这也会将您的非捕获组更改为捕获组,以便当在 replace() 中使用 RegExp 时,它可用于恢复 $ 之前的额外字符。

这是一个使用上述正则表达式匹配您提供的测试字符串的函数示例:

var testMatch = '$Not\\$yes $not $$' //Need double \\ because it is in a string literal.

//Note the use of a capture group and $1 in the replace string to retain the character
//  prior to the matching '$'.
result = testMatch.replace(/(^|[^\\$])\$(?!\$)/g,'$1_MATCH_');
console.log(result);

【讨论】:

  • 很遗憾,此代码无法运行 Google Apps 脚本。 “不完全支持 JavaScript 正则表达式功能的子集,例如捕获组和模式修饰符。” stackoverflow.com/questions/30968419/…
  • @JoeBob,您没有在问题中指定要在body.replaceText()body.findText() 中使用它。我专门为body.replaceText() 添加了代码。如果你想要别的东西,请在你的问题中明确。 [注意:原始代码在正常 RegExp 的 Google Apps 脚本中确实有效,但不是与 body.replaceText() 一起使用的正则表达式类型。正则表达式有许多种不同的定义,每一种都有不同的功能。当说“正则表达式”时,你真的必须说出要使用它的具体环境。]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多