【问题标题】:How to remove whitespace from right end of NSString?如何从 NSString 的右端删除空格?
【发布时间】:2015-10-05 03:12:01
【问题描述】:

这会从字符串的两端删除空格:

NSString *newString = [oldString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

如何从字符串的右端删除空格?

更新:来自accepted answer 的代码现在是SSToolkit 的一部分。耶!

【问题讨论】:

    标签: objective-c nsstring trim strip removing-whitespace


    【解决方案1】:

    更新:快速基准测试表明,基于Max' 和我的Matt's own adaption 表现最好。

    @implementation NSString (TrimmingAdditions)
    
    - (NSString *)stringByTrimmingLeadingCharactersInSet:(NSCharacterSet *)characterSet {
        NSUInteger location = 0;
        NSUInteger length = [self length];
        unichar charBuffer[length];    
        [self getCharacters:charBuffer];
    
        for (location; location < length; location++) {
            if (![characterSet characterIsMember:charBuffer[location]]) {
                break;
            }
        }
    
        return [self substringWithRange:NSMakeRange(location, length - location)];
    }
    
    - (NSString *)stringByTrimmingTrailingCharactersInSet:(NSCharacterSet *)characterSet {
        NSUInteger location = 0;
        NSUInteger length = [self length];
        unichar charBuffer[length];    
        [self getCharacters:charBuffer];
    
        for (length; length > 0; length--) {
            if (![characterSet characterIsMember:charBuffer[length - 1]]) {
                break;
            }
        }
    
        return [self substringWithRange:NSMakeRange(location, length - location)];
    }
    
    @end
    

    然后:

    NSString *trimmedString = [yourString stringByTrimmingTrailingCharactersInSet:[NSCharacterset whitespaceAndNewlineCharacterSet]];
    

    或用于前导空格:

    NSString *trimmedString = [yourString stringByTrimmingLeadingCharactersInSet:[NSCharacterset whitespaceAndNewlineCharacterSet]];
    

    它以抽象方式实现,因此您可以将它与任何可能的NSCharacterSetwhitespaceAndNewlineCharacterSet 一起使用。

    为方便起见,您可能需要添加这些包装方法:

    - (NSString *)stringByTrimmingLeadingWhitespace {
        return [self stringByTrimmingLeadingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    }
    
    - (NSString *)stringByTrimmingTrailingWhitespace {
        return [self stringByTrimmingTrailingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    }
    
    - (NSString *)stringByTrimmingLeadingWhitespaceAndNewline {
        return [self stringByTrimmingLeadingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    }
    
    - (NSString *)stringByTrimmingTrailingWhitespaceAndNewline {
        return [self stringByTrimmingTrailingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    }
    

    编辑:使用 charBuffer 恢复到初始版本以获得更好的性能。

    【讨论】:

    • 哇!谢谢!我喜欢使用 Cocoa 类别模式和此代码的模块化架构,但我正在寻找性能更好的解决方案。 "Optimize Your Text Manipulations" section of the "Cocoa Performance Guidelines" in the Xcode Documentation 不鼓励使用 characterAtIndex: 方法分别检索每个字符。
    • @MattDiPasquale:具有讽刺意味的是,我的第一个(预编辑)答案使用了unichar charBuffer。不知道为什么我删除它以支持characterAtIndex:。刚刚做了一个快速基准测试,你的版本比我最初的字节缓冲版本略快。
    • @Regexident,很好!很酷,看看如何使用char 缓冲区执行此操作。 1)您的代码是否处理多字节文本? (参见Stack Overflow answer to the Most efficient way to iterate over all the chars in an NSString 的第一条评论。2)对于stringByTrimmingTrailingCharactersInSet:characterSet,不应该将length &gt;= 0 更改为length &gt; 0|| length == 0 吗?否则,charBuffer[-1] 将在length == 0 时执行。 3) 我会使用substringFromIndexsubstringToIndex,而不是substringWithRange
    • 当然,length &gt; 0 是对的。固定的。至于它是否支持多字节字符串,我不知道。需要一些测试。一些快速的基准测试显示你的代码无论如何都优于我的代码。你的不会分配额外的内存,支持多字节,实际上比我的要快一点。老实说,我实际上正在考虑删除我的答案,因为它的投票(与您的答案相比)给人的印象是最好的方法是错误的。而且我假设大多数人不阅读 cmets(好吧,无论如何都要搞砸它们,但仍然......)。你支持删除吗,马特?
    【解决方案2】:

    基于@Regexident 和@Max 的答案,我想出了以下方法:

    @implementation NSString (SSToolkitAdditions)
    
    #pragma mark Trimming Methods
    
    - (NSString *)stringByTrimmingLeadingCharactersInSet:(NSCharacterSet *)characterSet {
        NSRange rangeOfFirstWantedCharacter = [self rangeOfCharacterFromSet:[characterSet invertedSet]];
        if (rangeOfFirstWantedCharacter.location == NSNotFound) {
            return @"";
        }
        return [self substringFromIndex:rangeOfFirstWantedCharacter.location];
    }
    
    - (NSString *)stringByTrimmingLeadingWhitespaceAndNewlineCharacters {
        return [self stringByTrimmingLeadingCharactersInSet:
                [NSCharacterSet whitespaceAndNewlineCharacterSet]];
    }
    
    - (NSString *)stringByTrimmingTrailingCharactersInSet:(NSCharacterSet *)characterSet {
        NSRange rangeOfLastWantedCharacter = [self rangeOfCharacterFromSet:[characterSet invertedSet]
                                                                   options:NSBackwardsSearch];
        if (rangeOfLastWantedCharacter.location == NSNotFound) {
            return @"";
        }
        return [self substringToIndex:rangeOfLastWantedCharacter.location+1]; // non-inclusive
    }
    
    - (NSString *)stringByTrimmingTrailingWhitespaceAndNewlineCharacters {
        return [self stringByTrimmingTrailingCharactersInSet:
                [NSCharacterSet whitespaceAndNewlineCharacterSet]];
    }
    
    @end
    

    这里是 GHUnit 测试,当然都通过了:

    @interface StringCategoryTest : GHTestCase
    @end
    
    @implementation StringCategoryTest
    
    - (void)testStringByTrimmingLeadingCharactersInSet {
        NSCharacterSet *letterCharSet = [NSCharacterSet letterCharacterSet];
        GHAssertEqualObjects([@"zip90210zip" stringByTrimmingLeadingCharactersInSet:letterCharSet], @"90210zip", nil);
    }
    
    - (void)testStringByTrimmingLeadingWhitespaceAndNewlineCharacters {
        GHAssertEqualObjects([@"" stringByTrimmingLeadingWhitespaceAndNewlineCharacters], @"", nil);
        GHAssertEqualObjects([@"\n \n " stringByTrimmingLeadingWhitespaceAndNewlineCharacters], @"", nil);
        GHAssertEqualObjects([@"\n hello \n" stringByTrimmingLeadingWhitespaceAndNewlineCharacters], @"hello \n", nil);
    }
    
    - (void)testStringByTrimmingTrailingCharactersInSet {
        NSCharacterSet *letterCharSet = [NSCharacterSet letterCharacterSet];
        GHAssertEqualObjects([@"zip90210zip" stringByTrimmingTrailingCharactersInSet:letterCharSet], @"zip90210", nil);
    }
    
    - (void)testStringByTrimmingTrailingWhitespaceAndNewlineCharacters {
        GHAssertEqualObjects([@"" stringByTrimmingLeadingWhitespaceAndNewlineCharacters], @"", nil);
        GHAssertEqualObjects([@"\n \n " stringByTrimmingLeadingWhitespaceAndNewlineCharacters], @"", nil);
        GHAssertEqualObjects([@"\n hello \n" stringByTrimmingTrailingWhitespaceAndNewlineCharacters], @"\n hello", nil);
    }
    
    @end
    

    我向SSToolkit 提交了一个 GitHub 拉取请求,并添加了这些方法。

    【讨论】:

    • 如果字符串实际上不是以集合中的任何字符开头,则您的第一个方法将失败,但它们稍后会出现在字符串中(它将从开头删除不应该出现的字符) .例如,在您的第一个测试中,如果您使用“90210zip”作为输入,它将返回“90210”而不是“90210zip”。 (实际上,您的尾随方法也是如此)。
    【解决方案3】:
    NSString* str = @"hdskfh   dsakjfh akhf kasdhfk asdfkjash fkadshf1234        ";
    NSRange rng = [str rangeOfCharacterFromSet: [NSCharacterSet characterSetWithCharactersInString: [str stringByReplacingOccurrencesOfString: @" " withString: @""]] options: NSBackwardsSearch];
    str = [str substringToIndex: rng.location+1];
    

    【讨论】:

      【解决方案4】:

      需要稍作改动以解决最后一个非尾随字符为多字节的情况:

      - (NSString *)stringByTrimmingTrailingCharactersInSet:(NSCharacterSet *)characterSet {
          NSRange rangeOfLastWantedCharacter = [self rangeOfCharacterFromSet:[characterSet invertedSet]
                                                                     options:NSBackwardsSearch];
          if (rangeOfLastWantedCharacter.location == NSNotFound) {
              return @"";
          }
          return [self substringToIndex:rangeOfLastWantedCharacter.location + rangeOfLastWantedCharacter.length]; // non-inclusive
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-10
        • 2018-01-09
        • 2011-11-29
        • 1970-01-01
        • 2018-10-01
        • 1970-01-01
        • 2012-03-06
        • 2020-08-15
        相关资源
        最近更新 更多