为什么我的 NSMutableString 编辑有时不起作用?

Why does my NSMutableString edit sometimes not work?

我正在尝试修复一些编号错误的电影字幕文件(每个子文件由一个空行分隔)。以下代码扫描测试文件中的错误字幕索引号。如果我只是 'printf' 错误的 old 索引和替换 new 索引,一切都会按预期显示。

//######################################################################
-(IBAction)scanToSubIndex:(id)sender
{
    NSMutableString* tempString = [[NSMutableString alloc] initWithString:[theTextView string]];
    int textLen = (int)[tempString length];
    
    NSScanner *theScanner = [NSScanner scannerWithString:tempString];
    while ([theScanner isAtEnd] == NO)
        {
        [theScanner scanUpToString:@"\r\n\r\n" intoString:NULL];
        [theScanner scanString:@"\r\n\r\n" intoString:NULL];
        
        if([theScanner scanLocation] >= textLen)
            break;
        else
            { // remove OLD subtitle index...
            NSString *oldNumStr;
            [theScanner scanUpToString:@"\r\n" intoString:&oldNumStr];
printf("old number:%s\n", [oldNumStr UTF8String]);                
            NSRange range = [tempString rangeOfString:oldNumStr];
                [tempString deleteCharactersInRange:range];

            // ...and insert SEQUENTIAL index
            NSString *newNumStr = [self changeSubIndex];
printf("new number:%s\n\n", [newNumStr UTF8String]);
                [tempString insertString:newNumStr atIndex:range.location];
            }
        }
printf("\ntempString\n\n:%s\n", [tempString UTF8String]);
}

//######################################################################
-(NSString*)changeSubIndex
{
    static int newIndex = 1;
    // convert int to string and return...
    NSString *numString = [NSString stringWithFormat:@"%d", newIndex];
        ++newIndex;
    
return numString;
}

然而,当我尝试将新索引写入静音字符串时,我最终得到了这样的无序结果:

sub 1

sub 2

sub 3

sub 1

sub 5

sub 6

sub 7

sub 5

sub 9

sub 7

sub 8

一个有趣的观察(和可能的线索?)是,当我到达 字幕编号 1000 时,每个数字都按要求按顺序写入可变字符串。我已经为此苦苦挣扎了几个星期,而且我在 SO 上找不到任何其他类似的问题。非常感谢任何帮助:-)

NSScanner & NSMutableString

NSMutableStringNSString 的子 class。换句话说,您可以在需要 NSString 的地方传递 NSMutableString。但这并不意味着您可以修改它。

scannerWithString: 预计 NSString。翻译成人类语言——我期待一个字符串,我也期待这个字符串是 read-only(不会被修改)。

换句话说 - 你的代码被认为是程序员错误 - 你给 NSScanner 一些东西,NSScanner 期望不可变的字符串并且你正在修改它。

我们不知道 NSScanner class 在幕后做了什么。可以有缓冲或任何其他类型的优化。

即使您幸运地使用了提到的 scanLocation 修复(在评论中),您也不应该依赖它,因为 底层实现 可以随任何新版本更改。

不要这样做。不仅在这里,而且在任何你看到不可变数据类型的地方。

(在某些情况下您可以这样做,但是您应该真正了解 幕后实现 正在做什么,确保它不会被修改,等等.但一般来说,除非你知道自己在做什么,否则这不是一个好主意。)

样本

此示例代码基于以下假设:

  • 我们正在谈论 SubRip 文本 (SRT)
  • 文件很小(可以轻松放入内存)
  • SRT 文件的其余部分是正确的
    • 尤其是分隔符 (@"\r\n")
@import Foundation;

NS_ASSUME_NONNULL_BEGIN

@interface SubRipText : NSObject

+ (NSString *)fixSubtitleIndexes:(NSString *)string;

@end

NS_ASSUME_NONNULL_END
@implementation SubRipText

+ (NSString *)fixSubtitleIndexes:(NSString *)string {
    NSMutableString *result = [@"" mutableCopy];
    
    __block BOOL nextLineIsIndex = YES;
    __block NSUInteger index = 1;
    
    [string enumerateLinesUsingBlock:^(NSString * _Nonnull line, BOOL * _Nonnull stop) {
        if (nextLineIsIndex) {
            [result appendFormat:@"%lu\r\n", (unsigned long)index];
            index++;
            nextLineIsIndex = NO;
            return;
        }
        
        [result appendFormat:@"%@\r\n", line];
        
        nextLineIsIndex = line.length == 0;
    }];
    
    return result;
}

@end

用法:

NSString *test = @"29\r\n"
"00:00:00,498 --> 00:00:02,827\r\n"
"Hallo\r\n"
"\r\n"
"4023\r\n"
"00:00:02,827 --> 00:00:06,383\r\n"
"This is two lines,\r\n"
"subtitles rocks!\r\n"
"\r\n"
"1234\r\n"
"00:00:06,383 --> 00:00:09,427\r\n"
"Maybe not,\r\n"
"just learn English :)\r\n";

NSString *result = [SubRipText fixSubtitleIndexes:test];

NSLog(@"%@", result);

输出:

1
00:00:00,498 --> 00:00:02,827
Hallo

2
00:00:02,827 --> 00:00:06,383
This is two lines,
subtitles rocks!

3
00:00:06,383 --> 00:00:09,427
Maybe not,
just learn English :)

还有其他方法可以实现此目的,但您应该考虑可读性、写入速度、运行 的速度,...取决于您的使用情况 - 您要使用多少种方法修复等