使用不可预测的值来防止正则表达式错误

时间:2013-06-17 10:43:08

标签: google-apps-script

在邮件合并应用程序中,我使用.replace()方法按自定义值替换字段标识符,并在反向过程中使用以获取标识符。

第一种方式每次都有效,因为替换第一个参数是一个非常正常的字符串,我是故意选择的...但是当我反转过程时,有时会发生字符串包含不正确的正则表达式字符。 这主要发生在+32 2 345 345形式的电话号码上,或者甚至包含一些突出的字符。

鉴于我无法防止这种情况发生,而且我没有希望我的最终用户不会使用这种电话号码格式,我想知道是否有人可以建议一个解决方法来逃避非法字符的出现?注意:它可以在字符串中的任何位置。

下面是两个函数的代码。

    ... (partial code)
    var newField = ChampSpecial(curData,realIdx,fctSpe);// returns the value from the database
    if(newField!=''){replacements.push(newField+'∏'+'#ch'+(n+1)+'#')};
//Logger.log('value in '+n+'='+realIdx+'  >>  '+Headers[realIdx]+'  =  '+ChampSpecial(curData,realIdx,fctSpe))
    app.getElementById('textField'+(n+1)).setHTML(ChampSpecial(curData,realIdx,fctSpe));
    if(e.parameter.source=='insertInText'){
    body.replaceText('#ch'+(n+1)+'#',newField);
    }
  }
  UserProperties.setProperty('replacements',replacements.join('|'));
  cloakOn();
  colorize('#ffff44');
  return app;
}

function fieldsInDoc(e){
  cloakOff();// remet d'abord les champs vides
  var replacements = UserProperties.getProperty('replacements').split('|');
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();
  for(var n=0;n<replacements.length;++n){
    var field = replacements[n].split('∏')[1];
    var testVal = replacements[n].split('∏')[0];    
    body.replaceText(testVal,field);
    }
  colorize('#ffff44');
}

2 个答案:

答案 0 :(得分:0)

在相反的过程中,您使用的字段值可以包含正则表达式特殊字符。你必须在更换前逃脱它们:

body.replaceText(field.replace(/[[\]{}()*-+?.,\\^$|#\s]/, '\\$&'), '#ch'+(n+1)+'#');

这就是说,&#34;取代标记&#34;一个坏主意。如果邮件合并的两个字段具有相同的值或文档模板中已存在替换文本,则会发生什么...

答案 1 :(得分:0)

一种可能的解决方案是防止doc中的示例字段包含正则表达式特殊字符,因此替换必须在正向过程中发生,不在反向(如其他答案中所建议的) 。 在字段值中转义这些字符值不起作用*因此我最终使用连字符进行简单替换(在大多数情况下更换斜杠或'+'是有意义的。)

(*)反向过程使用保留在内存中的值,因此转义符号会干扰该函数中的替换,从而阻止其正常工作。

最终的工作代码就像这样:

//(in the first function) 
    var newField = ChampSpecial(curData,realIdx,fctSpe).replace(/([*+?^=!:${}()|\[\]\/\\])/g, "-");// replace every occurrence of *+?^... by '-' (global search)

关于评论声明这种方法是一个坏主意我只能说我担心没有其他方法可以获得这种行为,并且如果因为主要使用邮件而最终导致错误的可能性合并是插入模板本身很少的专有名称,地址,电子邮件和电话号码。

至于字段指标,它们永远不会有相同的名称,因为它们是用数字索引的(#chXX#)。

编辑:根据Taras的评论,我会尝试其他解决方案,如果按预期工作,将会更新。


编辑6月19日 ,Yesssss ......找到了。

我终于找到了一个更好的解决方案,它不使用正则表达式,因此我不会被迫转义特殊字符... .find()方法接受任何字符串。 代码有点复杂,但结果值得痛苦: - ))

如果有人寻找类似的东西,这里是2个函数的完整代码。

function valuesInDoc(e){
  var lock = LockService.getPrivateLock(); // just in case one clicks the second button before this one ends
  var success = lock.tryLock(5000);
   if (!success) {
     Logger.log('tryLock failed to get the lock');
     return
   }
  colorize('#ffffff');// this function removes the color tags on the field marlers
  var app = UiApp.getActiveApplication(); 
  var listVal = UserProperties.getProperty('listSel').split(',');
  var replacements = [];
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();
  var find = body.findText('#ch');
  if(find == null){return app };
  var curData =   UserProperties.getProperty('selItem').split('|');
  var Headers = [];
  var OriHeaders = UserProperties.getProperty('Headers').split('|');
  for(n=0;n<OriHeaders.length;++n){
    Headers.push('#'+OriHeaders[n]+'#');
  }
  var fctSpe = 0 ;
  for(var i in Headers){if(Headers[i].indexOf('SS')>-1){fctSpe = i}}
  for(var n=0;n<listVal.length;++n){
    var realIdx = Number(listVal[n]);
  Logger.log(n);

    var newField = ChampSpecial(curData,realIdx,fctSpe);
//Logger.log(newField);    
    app.getElementById('textField'+(n+1)).setHTML(ChampSpecial(curData,realIdx,fctSpe));
    if(e.parameter.source=='insertInText'){
      var found = body.findText('#ch'+(n+1)+'#');// look for every field markers in the whole doc
      while(found!=null){
      var elemTxt = found.getElement().asText();
      var startOffset = found.getStartOffset();
      var len = ('#ch'+(n+1)+'#').length;
            elemTxt.deleteText(startOffset, found.getEndOffsetInclusive())
            elemTxt.insertText(startOffset,newField);// remove the marker and write the sample value in place
Logger.log('n='+n+'  newField = '+newField+'  for '+'#ch'+(n+1)+'#'+' at position '+startOffset)
  replacements.push(newField+'∏'+'#ch'+(n+1)+'#'+'∏'+startOffset);// memorize the change that just occured
        found = body.findText('#ch'+(n+1)+'#',found); //loop until all markers are replaced 

          }
        }
  }
  UserProperties.setProperty('replacements',replacements.join('|'));
  cloakOn();
  colorize('#ffff44');// colorize the markers if ever one is left but it shouldn't happen
  lock.releaseLock();
  return app;
}


function fieldsInDoc(e){
  var lock = LockService.getPrivateLock();
  var success = lock.tryLock(5000);
   if (!success) {
     Logger.log('tryLock failed to get the lock');
     return
   }
  cloakOff();// remet d'abord les champs vides > shows the hidden fields (markers that had no sample velue in the first function
  var replacements = UserProperties.getProperty('replacements').split('|');// recover replacement data as an array
Logger.log(replacements)
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();
  for(var n=replacements.length-1;n>=0;n--){ // for each replacement find the data in doc and write a field marker in place
    var testVal = replacements[n].split('∏')[0]; // [0] is the sample value
    if(body.findText(testVal)==null){break};// this is only to handle the case one click on the wrong button  trying to place markers again when they are already there ;-)
    var field = replacements[n].split('∏')[1];
    var testValLength = testVal.length;
    var found = body.findText(testVal);
    var startOffset = found.getStartOffset();
Logger.log(testVal+' = '+field+' / start: '+startOffset+' / Length: '+ testValLength)   
      var elemTxt = found.getElement().asText();
      elemTxt.deleteText(startOffset, startOffset+testValLength-1);// remove the text
//      elemTxt.deleteText(startOffset, found.getEndOffsetInclusive() )
      elemTxt.insertText(startOffset,field);// and write the marker
  }
  colorize('#ffff44'); // colorize the marker
  lock.releaseLock();// and release the lock
}