You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

正则表达式regex101测试正常 脚本执行时偶发匹配失效排查

问题描述

开发Google Apps Script脚本匹配ODT文档内的指定时间文本时,使用正则/(Hora|Horario) de (cierre|finalización): (\d\d(:|.)\d\d)/i在Regex101、RegExr等正则测试平台验证,绝大多数场景可正常命中,但脚本实际运行时偶发匹配失败:

  • 文本包含Hora de cierre: 11:57 horas时无法匹配
  • 格式几乎完全一致的Hora de cierre: 13:13 horas.可正常匹配
  • 手动传入完全相同的测试文本执行match方法可正常返回结果
    脚本逻辑为遍历Google Drive内的ODT格式文件,提取文档文本后执行正则匹配,手动硬编码的测试文本可正常匹配,初步怀疑故障出在Google Drive文档文本提取到正则执行的链路中。
原业务代码
function copiarResos() {
  let ass = SpreadsheetApp.getActiveSpreadsheet();
  let maxRows = ass.getSheetByName('set_de_datos_unificado').getLastRow();
  let columnaResos = ass.getSheetByName('set_de_datos_unificado').getSheetValues(2,2,maxRows,1);
  let columnaLinks = ass.getSheetByName('set_de_datos_unificado').getSheetValues(2,53,maxRows,1);
  let folder = DriveApp.getFolderById('folderID'); // 此处替换为实际文件夹ID
  let list = [];
  let files = folder.getFilesByType(MimeType.OPENDOCUMENT_TEXT);
  let match = '';
  const ENDTIMEREGEX = /(Hora|Horario) de (cierre|finalización): (\d\d(:|.)\d\d)/i ;
  let testText = "3. NOTIFICAR a las partes en este acto, a las víctimas por intermedio de la Fiscalía, a la OCSPP una vez que venza el plazo para recurrir y, oportunamente, COMUNICAR. /n Hora de cierre: 11:57 horas /n PALABRAS CLAVE: resolucion_interlocutoria  suspension_del_proceso_a_prueba_reanuda_plazos"
  while (files.hasNext()){
    file = files.next();
    list.push(file.getName().toString(), file.getId().toString());

  }
  for (let i = 0; i in columnaResos; i++){
    if(columnaLinks[i] == ''){
      let substring = columnaResos[i];
      match = list.find(element => {
        if (element.includes(substring)) {
          return true;
        } 
      });
      matchId = list.indexOf(match);
      if(matchId !== 0){
        let idString = list[matchId+1];
        let texto = driveTing(idString); // 加载文档文本到变量
        console.log(texto)
        let endTimeArr = texto.match(ENDTIMEREGEX);
        if(endTimeArr == null){
          console.log(endTimeArr)
        }else {
          let endTime = endTimeArr[0];
          console.log(endTimeArr)
          //endTime = endTime.replace("Horario de cierre: ","").replace("Hora de finalización: ", "").replace("Hora de cierre: ", "");
          //let endTimeInsert = ass.getSheetByName('set_de_datos_unificado').getRange(i+2 ,51);
          //endTimeInsert.setValue(endTime);
        }        
      }
    }
    match = '';
  };
}
function driveTing(fileId) {
  // 该函数用于将ODT文件转为临时Google Docs,提取文本后删除临时文件
  const id = Drive.Files.copy({title: "temp", mimeType: MimeType.GOOGLE_DOCS}, fileId).id;
  const doc = DocumentApp.openById(id);
  const header = doc.getHeader().getText();
  const text = header + doc.getBody().getText();
  DriveApp.getFileById(id).setTrashed(true)
  return text;
}
故障根因
  1. 不可见字符干扰:ODT文件转存为Google Docs的过程中,部分文档的冒号、数字、空格位置会混入零宽不可见字符(常见为零宽空格U+200B、字节顺序标记U+FEFF、软连字符U+00AD)。这类字符肉眼完全不可见,手动复制测试文本时不会被带入,但通过API提取的原始文本中会完整保留,直接打断正则的连续字符匹配逻辑,造成漏匹配。
  2. 正则容错性不足:原正则中分隔小时和分钟的.属于正则元字符,未做转义,理论上可匹配任意单字符,遇到特殊字符组合时可能出现非预期匹配;同时正则默认关键词、冒号、时间之间只能有单个半角空格,无法兼容多空格、制表符、不可见空白的场景。
  3. 隐藏代码bug:driveTing函数中直接调用doc.getHeader().getText(),如果目标文档没有页眉,getHeader()会返回null,直接调用方法会抛出脚本错误。
修复方案
  1. 增加文本清洗逻辑:在拿到提取的文档文本、执行正则匹配前,先移除所有零宽不可见字符,统一异常标点:
    let texto = driveTing(idString);
    // 移除所有零宽不可见字符、软连字符
    texto = texto.replace(/[\u200B-\u200D\uFEFF\u00AD]/g, '');
    // 统一全角/半角冒号,统一时间分隔符为半角冒号
    texto = texto.replace(/[::]/g, ':').replace(/(\d{1,2})[..](\d{2})/g, '$1:$2');
    
  2. 优化正则表达式:修正元字符问题,增加空白字符兼容,简化捕获分组:
    const ENDTIMEREGEX = /(?:Hora|Horario)\s+de\s+(?:cierre|finalización)\s*:\s*(\d{1,2}:\d{2})/i;
    
    优化点说明:
    • 不需要单独提取的分组改为非捕获分组(?:),减少冗余返回项
    • 关键词之间的单个空格替换为\s+,兼容多空格、制表符等空白场景
    • 冒号前后增加\s*匹配,兼容冒号前后误加空格的情况
    • 修正原正则未转义.的问题,明确匹配时间分隔符为冒号,避免非预期匹配
    • 直接捕获小时:分钟格式的时间值,后续不需要额外做多轮字符串替换
  3. 补全页眉空值兼容:修改driveTing函数,处理无页眉的文档场景:
    function driveTing(fileId) {
      const id = Drive.Files.copy({title: "temp", mimeType: MimeType.GOOGLE_DOCS}, fileId).id;
      const doc = DocumentApp.openById(id);
      const header = doc.getHeader() ? doc.getHeader().getText() : '';
      const text = header + doc.getBody().getText();
      DriveApp.getFileById(id).setTrashed(true)
      return text;
    }
    

内容的提问来源于stack exchange,提问作者IFritzler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 05:36:25