【问题标题】:How to split NSString where two or more whitespace characters are found?如何在找到两个或多个空白字符的地方拆分 NSString?
【发布时间】:2018-06-15 18:23:14
【问题描述】:

给定字符串输入:

@"bonus pay savings            2.69 F";
@"brick and mortar             0.15-B";

想要的输出字符串:

[@"bonus pay savings", @"2.69 F"];
[@"brick and mortar", @"0.15-B"];

我试过这种方法:

NSString * str = @"bonus pay savings            2.69 F";
NSArray * arr = [str componentsSeparatedByString:@"   "];
NSLog(@"Array values are : %@",arr);

但我的方法的缺点是我使用 3 个空格作为分隔符,而空格的数量可能会有所不同。如何实现?谢谢。

【问题讨论】:

    标签: objective-c nsstring


    【解决方案1】:

    您可以使用NSRegularExpression 来拆分您的字符串。让我们在NSString 上创建一个类别:

    NSString+asdiu.h

    @interface NSString (asdiu)
    
    - (NSArray<NSString *> *)componentsSeparatedByRegularExpressionPattern:(NSString *)pattern error:(NSError **)errorOut;
    
    @end
    

    NSString+asdiu.m

    @implementation NSString (asdiu)
    
    - (NSArray<NSString *> *)componentsSeparatedByRegularExpressionPattern:(NSString *)pattern error:(NSError **)errorOut {
        NSRegularExpression *rex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:errorOut];
        if (rex == nil) { return nil; }
    
        NSMutableArray<NSString *> *components = [NSMutableArray new];
        __block NSUInteger start = 0;
        [rex enumerateMatchesInString:self options:0 range:NSMakeRange(0, self.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
            NSRange separatorRange = result.range;
            NSRange componentRange = NSMakeRange(start, separatorRange.location - start);
            [components addObject:[self substringWithRange:componentRange]];
            start = NSMaxRange(separatorRange);
        }];
        [components addObject:[self substringFromIndex:start]];
        return components;
    }
    
    @end
    

    你可以这样使用它:

    NSArray<NSString *> *inputs = @[@"bonus pay savings            2.69 F", @"brick and mortar             0.15-B"];
    for (NSString *input in inputs) {
        NSArray<NSString *> *fields = [input componentsSeparatedByRegularExpressionPattern:@"\\s\\s+" error:nil];
        NSLog(@"fields: %@", fields);
    }
    

    输出:

    2018-06-15 13:38:13.152725-0500 test[23423:1386429] fields: (
        "bonus pay savings",
        "2.69 F"
    )
    2018-06-15 13:38:13.153140-0500 test[23423:1386429] fields: (
        "brick and mortar",
        "0.15-B"
    )
    

    【讨论】:

      【解决方案2】:

      使用正则表达式的简单解决方案。

      它将所有出现的 2 个或更多 ({2,}) 空白字符 (\\s) 替换为随机 UUID 字符串。然后它用那个 UUID 字符串分割字符串。

      NSString *separator = [NSUUID UUID].UUIDString; 
      NSString *string = @"bonus pay savings            2.69 F";
      NSString *collapsedString =  [string stringByReplacingOccurrencesOfString:@"\\s{2,}"
                                                            withString:separator
                                                               options:NSRegularExpressionSearch
                                                                 range:NSMakeRange(0, [string length])];
      NSArray *output = [collapsedString componentsSeparatedByString:separator];
      NSLog(@"%@", output);
      

      【讨论】:

        【解决方案3】:

        如果您可以假设输入字符串中只有 2 个字段,我将使用有限的拆分方法 like this one,它始终返回一个包含 2 个项目的数组,然后使用 @987654322 “修剪”第二个项目的空格@。

        【讨论】:

          【解决方案4】:

          @vadian 和@robmayoff 都提供了基于正则表达式 (RE) 的良好解决方案,在这两种情况下,正则表达式都用于匹配 间隙 以找到中断字符串的位置。为了进行比较,通过使用 RE 匹配您感兴趣的部分以另一种方式解决问题也是可能的。回复:

          \S+(\h\S+)*
          

          会匹配你感兴趣的文字,组成如下:

          \S          - match any non-space character, \S excludes both horizontal
                        (e.g. spaces, tabs) and vertical space (e.g. newlines)
          \S+         - one or more non-space characters, i.e. a "word" of sorts
          \h          - a single horizontal space character (if you wish matches to
                        span lines use \s - any horizontal *or* vertical space)
          \h\S+       - a space followed by a word
          (\h\S+)*    - zero or more space separated words
          \S+(\h\S+)* - a word follow by zero or more words
          

          通过这个简单的正则表达式,您可以使用matchesInString:options:range: 来获取NSTextCheckingResult 对象的数组,每个匹配一个对象;或者您可以使用enumerateMatchesInString:options:range:usingBlock: 在每次匹配时调用一个块。

          作为一个例子,这里是一个遵循@robmayoff 方法的解决方案:

          @interface NSString (componentsMatchingRegularExpression)
          
          - (NSArray<NSString *>*) componentsMatchingRegularExpression:(NSString *)pattern;
          
          @end
          
          @implementation NSString (componentsMatchingRegularExpression)
          
          - (NSArray<NSString *>*) componentsMatchingRegularExpression:(NSString *)pattern
          {
             NSError *errorReturn;
             NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&errorReturn];
          
             if (!regularExpression)
                return nil;
          
             NSMutableArray *matches = NSMutableArray.new;
             [regularExpression enumerateMatchesInString:self
                                                 options:0
                                                   range:NSMakeRange(0, self.length)
                                              usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop)
                                                        {
                                                           [matches addObject:[self substringWithRange:result.range]];
                                                        }
             ];
          
             return matches.copy; // non-mutable copy
          }
          
          @end
          

          是否匹配您希望保留或删除的内容更好是主观的,请自行选择。

          【讨论】:

            【解决方案5】:

            正则表达式对此很好,使用它们给出的解决方案也很好,但为了完成,你也可以使用 NSScanner 来完成,它几乎总是比正则表达式有更好的性能,而且很容易习惯如果您需要进行更复杂的文本解析,请使用。

            NSString *str = @"bonus pay savings            2.69 F";
            NSScanner *scanner = [NSScanner scannerWithString:str];
            scanner.charactersToBeSkipped = nil; // default is to ignore whitespace
            while (!scanner.isAtEnd) {
                NSString *name;
                NSString *value;
                // scan up to two spaces, this would be the name
                [scanner scanUpToString:@"  " intoString:&name];
            
                // scan the two spaces and any extra whitespace
                [scanner scanCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:nil];
            
                // scan to the end of the line, this is the value
                [scanner scanUpToString:@"\n" intoString:&value];
            }
            

            【讨论】:

              猜你喜欢
              • 2013-09-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-07-29
              • 2014-05-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多