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

Word JS分句函数网页版Range.expandTo报错,桌面版正常求助

Word网页版中Range.expandTo导致realsentenizer函数崩溃的修复方案

问题描述

realsentenizer函数在桌面版Microsoft® Word for Microsoft 365 MSO(版本2309 Build 16.0.16827.20130 64位)运行完全正常,但在网页版Office(已测试最新版Chrome和Edge)里会崩溃。崩溃点在console.log('break15.5')之后、console.log('break16')之前的await context.sync()语句,调试信息显示问题出在Range.expandTo方法上。

这个函数的作用是按.分割文档正文生成句子,同时保护clause 5.6、registration no. 555这类不该分割的含.场景。

问题根源

网页版Word的JavaScript API在处理文档结构变更后的Range对象时,和桌面版存在兼容性差异:

  1. 函数先通过替换操作保护特殊点,之后又恢复原文本,这个过程改变了文档结构
  2. 原代码复用了之前获取的sentences数组中的Range对象,这些对象在文档结构变更后已经失效,网页版API调用expandTo时无法识别失效的Range,从而触发崩溃
  3. 原代码else分支里的saved_range = sentences.items[i+1]存在数组越界风险,网页版对这类错误的容错性更低

修复方案

1. 恢复保护文本后重新获取句子Range

不再复用之前的sentences对象,在恢复所有保护文本后,重新调用getTextRanges获取最新的句子Range集合,确保所有Range都是基于当前文档结构的有效引用。

2. 修复循环越界问题

在else分支中添加边界判断,避免i+1超出数组长度导致的错误。

3. 优化Range合并逻辑

减少对旧Range对象的依赖,确保每次合并操作都使用最新的有效Range。

修改后的完整代码

var global_sentences3_text = []
var global_sentences3_ranges = []
var global_sentences_real = null
var global_contract_body = null
async function realsentenizer() {
  await Word.run(async (context) => {
    const body = context.document.body.getRange();
    body.load("text");
    await context.sync();

    console.log('break1')
    // 保存全文本用于后续API分析
    global_contract_body = body.text

    console.log('break2')

    /// 保护特殊点开始 ///
    // 按顺序处理,避免重复匹配同一内容导致重复恢复
    var saved_text = []
    var ranges_to_protect = body.search("[0-9].[0-9]", {matchWildcards: true, matchCase: false})
    console.log('break3')
    ranges_to_protect.load('text')
    console.log('break4')
    await context.sync()
    console.log('break5')
    for (let i = 0; i < ranges_to_protect.items.length ; i++) {
      saved_text.push(ranges_to_protect.items[i].text)
      ranges_to_protect.items[i].insertText('[PROTECTEDDOT]','Replace')
    }
    console.log('break6')
    await context.sync()
    
    var saved_text2 = []
    var ranges_to_protect2 = body.search("[Nn][Oo].", {matchWildcards: true, matchCase: false})
    ranges_to_protect2.load('text')
    await context.sync()
    for (let i = 0; i < ranges_to_protect2.items.length ; i++) {
      saved_text2.push(ranges_to_protect2.items[i].text)
      ranges_to_protect2.items[i].insertText('[PROTECTEDDOT]','Replace')
    }
    await context.sync()
    console.log('break7')
    
    var saved_text3 = []
    var ranges_to_protect3 = body.search("[0-9].", {matchWildcards: true, matchCase: false})
    ranges_to_protect3.load('text')
    await context.sync()
    for (let i = 0; i < ranges_to_protect3.items.length ; i++) {
      saved_text3.push(ranges_to_protect3.items[i].text)
      ranges_to_protect3.items[i].insertText('[PROTECTEDDOT]','Replace')
    }
    await context.sync()
    console.log('break8')

    var saved_text4 = []
    var ranges_to_protect4 = body.search("[Cc][Ff].", {matchWildcards: true, matchCase: false})
    ranges_to_protect4.load('text')
    await context.sync()
    for (let i = 0; i < ranges_to_protect4.items.length ; i++) {
      saved_text4.push(ranges_to_protect4.items[i].text)
      ranges_to_protect4.items[i].insertText('[PROTECTEDDOT]','Replace')
    }
    await context.sync()
    console.log('break9')

    var saved_text6 = []
    var ranges_to_protect6 = body.search("[A-Za-z].[A-Za-z]", {matchWildcards: true, matchCase: false})
    ranges_to_protect6.load('text')
    await context.sync()
    for (let i = 0; i < ranges_to_protect6.items.length ; i++) {
      saved_text6.push(ranges_to_protect6.items[i].text)
      ranges_to_protect6.items[i].insertText('[PROTECTEDDOT]','Replace')
    }
    await context.sync()
    console.log('break10')

    /// 保护特殊点结束 ///

    // 替换含whereas和;的句子中的;为.,避免生成过长的引言句
    const pseudo_sentences = body.getTextRanges([" ."], false)
    pseudo_sentences.load('text')
    await context.sync()

    console.log('break11')
    
    var ranges_to_protect5 = []
    var searched_ranges = null
    for (let i = 0; i < pseudo_sentences.items.length ; i++) {
      if (pseudo_sentences.items[i].text.toLowerCase().includes('whereas') && pseudo_sentences.items[i].text.includes(';')){
        searched_ranges = pseudo_sentences.items[i].search(";", {matchWildcards: true, matchCase: false})
        searched_ranges.load('text')
        await context.sync()
        for (let j = 0; j < searched_ranges.items.length ; j++) {
          ranges_to_protect5.push(searched_ranges.items[j])
          searched_ranges.items[j].insertText('.','Replace')
        }
      }
    }
    await context.sync();
    console.log('break12')

    // 分割文本获取句子Range
    const sentences = body.getTextRanges([" ."], false);
    sentences.load('text')
    global_sentences_real = sentences
    await context.sync()

    console.log('break13')

    // 恢复保护的文本
    for (let i = 0; i < ranges_to_protect.items.length ; i++) {
      ranges_to_protect.items[i].insertText(saved_text[i],'Replace')
    }
    for (let i = 0; i < ranges_to_protect2.items.length ; i++) {
      ranges_to_protect2.items[i].insertText(saved_text2[i],'Replace')
    }
    for (let i = 0; i < ranges_to_protect3.items.length ; i++) {
      ranges_to_protect3.items[i].insertText(saved_text3[i],'Replace')
    }
    for (let i = 0; i < ranges_to_protect4.items.length ; i++) {
      ranges_to_protect4.items[i].insertText(saved_text4[i],'Replace')
    }
    for (let i = 0; i < ranges_to_protect5.length ; i++) {
      ranges_to_protect5[i].insertText(";",'Replace')
    }
    for (let i = 0; i < ranges_to_protect6.items.length ; i++) {
      ranges_to_protect6.items[i].insertText(saved_text6[i],'Replace')
    }
    await context.sync() 

    console.log('break14')

    // 关键修复:恢复文本后重新获取最新的句子Range,避免使用失效的旧Range
    const fresh_sentences = body.getTextRanges([" ."], false);
    fresh_sentences.load('text');
    await context.sync();

    const new_ranges =[]
    let saved_range = fresh_sentences.items[0]
    let extended_range = {};

    console.log('break15')

    console.log(fresh_sentences.items.length)
    // 遍历合并相邻Range
    for (let i = 1; i < fresh_sentences.items.length ; i++) {
      console.log(`ITERATION: ${i}`)
      console.log('for_start')
      if (!fresh_sentences.items[i].text.includes(" .")){
        console.log('if_1')
        extended_range = saved_range.expandTo(fresh_sentences.items[i])
        console.log('if_2')
        extended_range.load('text')
        console.log('if_3')
        saved_range = extended_range
        console.log('if_4')
      } else {
        extended_range = saved_range.expandTo(fresh_sentences.items[i])
        console.log('else_1')
        extended_range.load('text')
        console.log('else_2')
        new_ranges.push(extended_range)
        console.log('else_3')
        // 修复越界问题:判断i+1是否在数组范围内
        if (i+1 < fresh_sentences.items.length) {
          saved_range = fresh_sentences.items[i+1]
        } else {
          // 处理最后一个元素的情况
          break;
        }
        console.log('else_4')
      }
      console.log('for_end')      
    }   
    console.log('LOOP END FULL')  
    // 确保最后一个Range被加入
    if (saved_range) {
      new_ranges.push(saved_range)
    }
    
    console.log('break15.5')
    await context.sync();
    
    console.log('break16')

    /// 处理全大写标题逻辑 ///

    var new_ranges2 = []
    for (let i = 0; i < new_ranges.length ; i++) {
      new_ranges[i].load('text')
      await context.sync(); // 先加载文本再判断
      if (new_ranges[i].text.toUpperCase() === new_ranges[i].text){
        if (i+1 < new_ranges.length) { // 避免越界
          const new_range = new_ranges[i].expandTo(new_ranges[i+1])
          new_range.load('text')
          new_ranges2.push(new_range)
          i += 1
        } else {
          new_ranges2.push(new_ranges[i])
        }
      } else {
        new_ranges2.push(new_ranges[i])
      }
    }
    await context.sync()

    console.log('break17')

    /// 处理全大写标题逻辑结束 ///

    for (let sentence of new_ranges2) {
      sentence.load('text')
      await context.sync();
      global_sentences3_text.push(sentence.text)
      sentence.track()
      global_sentences3_ranges.push(sentence)
    }
    await context.sync()
    
    for (let sentence of global_sentences3_text) {
      console.log(sentence)
    }

    console.log('break18')

    global_sentences_real.track()
    
    await context.sync()
    console.log('成功完成分句')
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:33:09