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

NSMutableString编辑失效问题:字幕编号修复异常求助

问题分析与修复方案

你的问题根源在于错误地使用了全局查找rangeOfString:来定位要替换的字幕编号,同时修改字符串后没有同步更新扫描器的状态,导致后续操作和字符串实际内容完全脱节。

为什么会出现混乱?

  1. 当你用[tempString rangeOfString:oldNumStr]时,它会返回整个字符串中第一个匹配oldNumStr的位置,而不是当前扫描器指向的那个编号。比如当处理到后面的编号1时,它会匹配到第一个字幕的1,导致错误地替换前面的编号。
  2. 每次修改tempString(删除旧编号、插入新编号)后,theScanner的扫描位置还是基于原来的字符串长度计算的,这就导致扫描位置和字符串实际内容的位置完全不匹配,后续的扫描和替换操作全乱了。
  3. 至于编号到1000时正常,只是巧合:当所有编号都是4位时,rangeOfString:不会误匹配更短的数字(比如1000不会和1、10、100匹配),所以全局查找刚好命中正确的位置,但这只是特殊情况,不是正确的解决思路。

修复方案一:基于扫描器位置精准定位替换范围

我们不需要全局查找,而是直接通过扫描器的位置来确定当前要替换的编号的范围,这样就能精准操作当前字幕的编号:

-(IBAction)scanToSubIndex:(id)sender {
    NSMutableString* tempString = [[NSMutableString alloc] initWithString:[theTextView string]];
    NSScanner *theScanner = [NSScanner scannerWithString:tempString];
    int currentIndex = 1;
    
    while (![theScanner isAtEnd]) {
        // 跳过前面的空行分隔符(第一个字幕前可能没有)
        [theScanner scanUpToString:@"\r\n\r\n" intoString:NULL];
        if ([theScanner scanString:@"\r\n\r\n" intoString:NULL]) {
            // 跳过分隔符后,当前位置就是下一个字幕的编号开头
        }
        
        // 记录当前编号的起始位置
        NSInteger numStartLocation = [theScanner scanLocation];
        // 扫描出当前的旧编号
        NSString *oldNumStr;
        if (![theScanner scanUpToString:@"\r\n" intoString:&oldNumStr]) {
            break; // 没有更多编号了
        }
        // 计算旧编号的范围:从起始位置到扫描后的位置
        NSRange numRange = NSMakeRange(numStartLocation, [theScanner scanLocation] - numStartLocation);
        
        // 替换旧编号为新的顺序编号
        NSString *newNumStr = [NSString stringWithFormat:@"%d", currentIndex++];
        [tempString replaceCharactersInRange:numRange withString:newNumStr];
        
        // 同步扫描器位置:因为替换后字符串长度变化了,需要重新设置扫描位置
        [theScanner setScanLocation:numStartLocation + [newNumStr length]];
    }
    
    // 最后更新文本视图
    [theTextView setString:tempString];
    NSLog(@"处理完成后的字幕:\n%@", tempString);
}

修复方案二:分割字幕块逐个处理(更清晰易维护)

另一种更稳妥的方式是先把整个文本分割成单个的字幕块,然后逐个修改每个块的编号,最后再拼接起来:

-(IBAction)scanToSubIndex:(id)sender {
    NSString *originalText = [theTextView string];
    // 按空行分割成字幕块(注意处理可能的换行符差异,比如\n\n或者\r\n\r\n)
    NSArray *subtitleBlocks = [originalText componentsSeparatedByString:@"\r\n\r\n"];
    
    NSMutableArray *processedBlocks = [NSMutableArray array];
    int currentIndex = 1;
    
    for (NSString *block in subtitleBlocks) {
        if ([block length] == 0) {
            [processedBlocks addObject:block];
            continue;
        }
        // 分割块的第一行(编号)和后面的内容
        NSRange firstLineRange = [block rangeOfString:@"\r\n"];
        if (firstLineRange.location == NSNotFound) {
            // 没有换行的块,直接添加编号?按实际情况处理
            NSString *newBlock = [NSString stringWithFormat:@"%d\r\n%@", currentIndex++, block];
            [processedBlocks addObject:newBlock];
        } else {
            NSString *contentPart = [block substringFromIndex:firstLineRange.location + firstLineRange.length];
            NSString *newBlock = [NSString stringWithFormat:@"%d\r\n%@", currentIndex++, contentPart];
            [processedBlocks addObject:newBlock];
        }
    }
    
    // 拼接回完整文本
    NSString *finalText = [processedBlocks componentsJoinedByString:@"\r\n\r\n"];
    [theTextView setString:finalText];
    NSLog(@"处理完成后的字幕:\n%@", finalText);
}

为什么这两种方案能解决问题?

  • 方案一通过扫描器的位置直接定位当前要修改的编号范围,避免了全局查找的误匹配,同时每次修改后同步更新扫描器的位置,保证后续操作和字符串内容一致。
  • 方案二把问题拆解为处理单个字幕块,逻辑更清晰,完全避免了扫描器和字符串不同步的问题,也更容易调试和扩展。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 19:42:39