【问题标题】:Finding position of a character related to the position of another character in NSString在 NSString 中查找与另一个字符的位置相关的字符的位置
【发布时间】:2015-11-26 15:20:52
【问题描述】:

假设我有一个txt文件,数据如下;

12345,123,98765,JOHN,RL,s/w
12345,123,98765,SAM,RL,s/w 
12345,123,98765,MIC,RL,s/w
12345,123,98765,MIC,RL,s/w
12345,123,98765,MIC,RL,s/w
12345,123,98765,MIC,RL,s/w
12345,123,98765,MIC,RL,s/w
12345,123,98765,AMI,RL,s/w
12345,123,98765,THIMOTHY,RL,s/w

它捆绑在我的应用程序中,我正在将数据读取到 nsstring。我正在传递一个搜索词,比如“MIC”。当我通过这个搜索词时,我想获取具有搜索词的行并从字符串中删除所有其他行。

我尝试了以下代码:

NSString *searchTerm = @"MIC";
    NSRange rangess = [dataToBeParsed rangeOfString:searchTerm];

    NSRange range = [dataToBeParsed rangeOfString:@"" options:NSBackwardsSearch range:rangess];
    NSLog(@"range.location: %lu", range.location);
    NSString *substring = [dataToBeParsed substringFromIndex:range.location+1];
    NSLog(@"substring: \n%@'", substring);

我能够找到搜索词的位置并清除字符串内容直到搜索位置。但我想得到完整的线路。

注意:在我的原始文档中,具有搜索词的行将与文档中一样连续列出。

请帮忙。

【问题讨论】:

  • 将其保存在字符串中是不正确的。你想要一个由字符串数组组成的数组。
  • 您可以使用正则表达式:^(.*(?:MIC).*)$。只需将MIC 替换为您要查找的任何内容。评估这个表达式会给你完整的行。
  • 正如@trojanfoe 所说,使用 NSArray 的 NSArray 应该会更好,尤其是当它似乎是“以逗号作为分隔符”的 CSV 时。
  • 是的,我明白你的意思。但是我的原始文档将缺少 2 个以上的条目。所以使用 componentsSeperatedByString:@"\n" 加载到数组中需要时间。
  • @akashivskyy 也尝试过,但我们的目标是针对 iPod touch 第 4 代设备的应用程序。所以它需要更多的时间。 :(

标签: ios objective-c nsstring substring nsrange


【解决方案1】:
NSString *string        = stringDataToBeParsed;
NSArray *components     = [string componentsSeparatedByString:@"\n"];
NSMutableArray *array   = [NSMutableArray new];

for (NSString *string in components)
{
    if ([string containsString:searchTerm])
    {
        [array addObject:string];
    }
}

NSString *stringWithSearchLinesOnly = [array componentsJoinedByString:@"\n"];

如果上面的速度还不够快,那么在 Objective-c 中你真的不能比这更快:

NSString *string             = stringDataToBeParsed;
NSMutableArray *mutableArray = [NSMutableArray new];

[string enumerateLinesUsingBlock:^(NSString * _Nonnull line, BOOL * _Nonnull stop)
{
    if ([line containsString:searchTerm])
    {
        [mutableArray addObject:line];
    }
}];

NSString *stringWithSearchLinesOnly = [mutableArray componentsJoinedByString:@"\n"];

【讨论】:

  • 感谢您的回复。我试过这种方法,但问题是,由于 csv 文件有超过 2 行缺少行,循环遍历所有这些行需要更多时间。
  • 这就是我们最终的做法。仍然需要大约 15 秒才能完成操作。
  • 是的,您不会在 Objective-C 中获得更快的速度。上面代码中唯一不必要/低效的操作是在行尾添加回终止符。 enumerateLinesUsingBlock 非常高效,因为它在您现有的对象上进行操作,不需要创建新对象。如果您在创建时指定大小,则添加到可变数组可以快得多,但您事先不知道大小。尽管如此,如果您已经知道至少会有 x 行 - 如果您指定 [NSMutableArray arrayWithCapacity:x]; 可能会略微提高性能
  • 我的意思是我已经尝试过这个方法。但我要问的是,假设我有一个字符串并且我正在传递一个搜索词。我可以找到搜索词的位置。但我不会得到那条完整的线。所以我试图在搜索词位置之前获得最后一个“\ n”位置并获得整行。这就是我在我的问题中提出的问题。有什么想法吗?
  • 您真的应该找出为什么发布的解决方案对您来说如此缓慢。不应该。寻找行终止符的字符串扫描要昂贵得多。
【解决方案2】:

我假设有两件事会导致最大的成本:

  • 一遍又一遍地搜索整个字符串

  • 为部分字符串创建实例。

但是,您可以通过在整个字符串上使用范围而不是创建子字符串来摆脱这两种情况:

NSUInteger length = [source length];
NSRange searchRange = NSMakeRange( 0, length );
NSRange hitRange = NSMakeRange( 0, 0 );

// Iterate over the string unless you do not find any hit
while( (hitRange = [source rangeOfString:@"Mic" options:0 range:searchRange]).length != 0)
{
   // hitRange contains the range we found "Mic". We have to enlarge it on both sides to \n
   NSRange lineRange = [source rangeOfString:@"\n" options:NSBackwardSearch range:NSMakeRange( 0, hitRange.location ); // Maybe it is an optimization to cut the range ahead of the hit to the last hit. But since it is a backward search, I do not think that this has any effect.

   // We can enlarge the range to the prev \n:
   hitRange.length = hitRange.range + hitRange.location - lineRange.location +1;

   // Go the the next \n
   lineRange = [source rangeOfString:@"\n" options:0 range:NSMakeRange( hitRange.location+hitRange.length, length-hitRange.location+hitRange.length)];

   hitRange.length = lineRange.location-hitRange.location;

   // Do something with the line
   …

   // Move forward
   searchRange.location = hitRange.location+hitRange.length;
   searchRange.length = length-searchRange.location;
}

在 Safari 中输入,只是为了展示方法。肯定有 +1/-1 错误,并进行了一些优化以减少对单个字符的搜索。

显然,这只有在结尾和开头都有 \n 时才有效。如果不是,请添加 \n。

我真的不知道,这是否更快。不过,值得一试。

作为补充,您可以在 NSString 的类别中创建一个方法,并从中取出一个块,该块采用范围。这会让它更通用。

修改后的代码:

NSUInteger length = [dataToBeParsedFirst length];
NSRange searchRange = NSMakeRange( 0, length );
NSRange hitRange = NSMakeRange( 0, 0 );
NSMutableArray *linesArr =[[NSMutableArray alloc]init];

// Iterate over the string unless you do not find any hit
while( (hitRange = [dataToBeParsedFirst rangeOfString:ItemID options:0 range:searchRange]).length != 0)
{
    NSRange lineRange = [dataToBeParsedFirst rangeOfString:@"\n" options:NSBackwardsSearch range:NSMakeRange( 0, hitRange.location )];

    NSRange lineEndRange = [dataToBeParsedFirst rangeOfString:@"\n" options:0 range:NSMakeRange(hitRange.location, 500)];

    NSRange dataRange = NSMakeRange(lineRange.location + lineRange.length, lineEndRange.location - lineRange.location + lineRange.length - 1);

    [linesArr addObject:[[dataToBeParsedFirst substringWithRange:dataRange] substringToIndex:dataRange.length - 1]];

    // Move forward
    searchRange.location = lineEndRange.location+lineEndRange.length;
    searchRange.length = length-searchRange.location;
}

NSString *dataToBeParsed = [[linesArr componentsJoinedByString:@"\n"] stringByAppendingString:@"\n"];

【讨论】:

  • 非常感谢@amin。这就是我想要做的。我对代码做了一些修改,它按我的预期工作!! :) 早些时候,完成搜索操作大约需要 15 秒。现在只需 8 秒!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-22
  • 2012-12-24
  • 1970-01-01
  • 2011-01-23
相关资源
最近更新 更多