如何从NSString的右端删除空格?

20

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

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

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

更新:被接受的答案中的代码现在已成为SSToolkit的一部分。耶!

4个回答

32

更新: 快速的基准测试显示Matt自己的适应,基于Max和我的方法,效果最佳。

@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]];

它的实现是以抽象方式实现的,因此您可以将其与任何可能的NSCharacterSet一起使用,whitespaceAndNewlineCharacterSet只是其中之一。

为了方便,您可能想要添加这些包装方法:

- (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类别模式和模块化架构,但我正在寻找一个性能更好的解决方案。在Xcode文档中的“Cocoa性能指南”中的“优化文本操作”部分不建议使用characterAtIndex:方法逐个检索每个字符。链接 - ma11hew28
具有讽刺意味的是,我的第一个(未经编辑的)答案使用了 unichar charBuffer。不知道为什么我放弃了它,转而使用了 characterAtIndex:。我刚做了一个快速基准测试,你的版本比我的初始字节缓冲版本略快。 - Regexident
@Regexident, 好棒!很酷可以看到如何使用“char”缓冲区来实现这个。1)您的代码是否处理多字节文本?(请参见此Stack Overflow答案上的第一条评论,以了解如何最有效地迭代NSString中的所有字符)。2)对于stringByTrimmingTrailingCharactersInSet:characterSetlength >= 0应该改为length> 0,并且应该删除|| length == 0吗?否则,当length == 0时,charBuffer [-1]将被执行。3)我会使用substringFromIndexsubstringToIndex而不是substringWithRange - ma11hew28
你说的 length > 0 是对的,当然。已经修复了。至于它是否支持多字节字符串,我不知道。需要进行一些测试。一些快速基准测试显示,你的代码比我的更优秀。你的代码不会分配额外的内存,支持多字节,并且实际上比我的稍微快一点。老实说,我正在考虑删除我的答案,因为它的投票(与你的答案相比)给出了关于什么是最佳方法的错误印象。而且我认为大多数人不会阅读评论(好吧,反正他们也没什么用,但还是……)。Matt,你支持删除吗? - Regexident
哈哈...谢谢反馈。我开始觉得自己过于自大地接受了自己的答案。我把“值得注意”的部分移到了你的答案顶部。我认为这应该足够好了。这样,人们可以看到你的贡献,并且还可以学习如何使用char缓冲区。但是,如果你更喜欢删除它(或以不同的方式编辑它),那对我来说也没问题。你认为什么最好就怎么做。 - ma11hew28

18

在@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

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


如果字符串实际上不以集合中的任何字符开头,但它们后来出现在字符串中,您的第一个方法将失败(它将删除不应该删除的开头字符)。例如,在您的第一个测试中,如果您使用“90210zip”作为输入,则会返回“90210”而不是“90210zip”。(实际上,您的尾随方法也是如此。) - lnafziger

7
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];

1
需要稍微更改以考虑最后一个非尾随字符为多字节的情况:
- (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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接