122

Consider the following example:

"    Hello      this  is a   long       string!   "

I want to convert that to:

"Hello this is a long string!"
hfossli
  • 22,616
  • 10
  • 116
  • 130

13 Answers13

124

OS X 10.7+ and iOS 3.2+

Use the native regexp solution provided by hfossli.

Otherwise

Either use your favorite regexp library or use the following Cocoa-native solution:

NSString *theString = @"    Hello      this  is a   long       string!   ";

NSCharacterSet *whitespaces = [NSCharacterSet whitespaceCharacterSet];
NSPredicate *noEmptyStrings = [NSPredicate predicateWithFormat:@"SELF != ''"];

NSArray *parts = [theString componentsSeparatedByCharactersInSet:whitespaces];
NSArray *filteredArray = [parts filteredArrayUsingPredicate:noEmptyStrings];
theString = [filteredArray componentsJoinedByString:@" "];
Community
  • 1
  • 1
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • 4
    I'd be curious of a performance comparison of this to a regex replacement with a trim to remove the ends. On the one hand, you have a regex to deal with. On the other, you have a predicate. Either requires internal processing of the respective expressions. – lilbyrdie Jun 23 '11 at 15:30
  • @lilbyrdie: This depends on the string I think, how many whitespaces there are. My solution is quite slow, because it creates a new object for every substring and sends method calls to each of them. – Georg Schölly Jun 23 '11 at 18:55
  • 2
    Fine answer, upvoted as such, but I challenge your definition of "easy". Sincerely, former Python guy now in ObjC-land ;-) – JK Laiho May 31 '12 at 09:17
  • @JKLaiho: Considering that most other solutions need external libraries I'd say this is easy. :) I think they are going to abandon obj-c in the long run and replacing it with a much more modern language. Take a look at the [MacRuby](http://macruby.org) project! – Georg Schölly Jun 01 '12 at 21:44
  • 2
    You made me laugh with 'don't use complex solutions if there's an easy one'. So the easiest one is [toBeTrimmed stringByReplacingOccurrencesOfString:@" " withString:@""] no? I still upvote your answer but it's definitely the easiest – Mário Carvalho Jun 09 '13 at 09:08
  • 2
    @MárioCarvalho The question asks how to remove *excess* whitespace, not all of it. – swilliams Jul 01 '13 at 19:42
  • I don't really see why splitting into array and using predicate is worse than regex. It's not more readable code. I'm curious about performance both in regex-solution and array-component-solution – hfossli Aug 07 '13 at 10:17
  • @hfossli: The fact that you have to add a library and possible keep updated is the complicated part. Personally, I'd probably use regexp if it was available, because it expresses your intent much better. – Georg Schölly Aug 19 '13 at 13:45
  • @GeorgSchölly Regex is available in NSFoundstion since iOS 4. The question is not about solutions for iOS 3.2. There is no need for adding a library. – hfossli Aug 19 '13 at 17:26
  • @GeorgSchölly I've added performance results to my answer. Regex uses half the time. – hfossli Oct 18 '13 at 09:47
  • @hfossli: I'm not suprised by your findings, my method uses lots of additional memory. Would you mind including your solution in my answer? I'll change it to a community wiki if you agree. – Georg Schölly Oct 18 '13 at 14:11
53

Regex and NSCharacterSet is here to help you. This solution trims leading and trailing whitespace as well as multiple whitespaces.

NSString *original = @"    Hello      this  is a   long       string!   ";

NSString *squashed = [original stringByReplacingOccurrencesOfString:@"[ ]+"
                                                         withString:@" "
                                                            options:NSRegularExpressionSearch
                                                              range:NSMakeRange(0, original.length)];

NSString *final = [squashed stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

Logging final gives

"Hello this is a long string!"

Possible alternative regex patterns:

  • Replace only space: [ ]+
  • Replace space and tabs: [ \\t]+
  • Replace space, tabs and newlines: \\s+

Performance rundown

Ease of extension, performance, number lines of code and the number of objects created makes this solution appropriate.

Community
  • 1
  • 1
hfossli
  • 22,616
  • 10
  • 116
  • 130
41

Actually, there's a very simple solution to that:

NSString *string = @" spaces in front and at the end ";
NSString *trimmedString = [string stringByTrimmingCharactersInSet:
                                  [NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(@"%@", trimmedString)

(Source)

pasawaya
  • 11,515
  • 7
  • 53
  • 92
arikfr
  • 3,311
  • 1
  • 25
  • 28
  • 29
    I think that this will eliminate only leading and trailing spaces, and eliminate all of them. it won't deal with "hello foo" – Brian Postow Sep 15 '09 at 14:09
  • 2
    d*mn line endings and auto-format... it doesn't deal with "hello______foo" (assume _ -> " " because formatting comments is hard) – Brian Postow Sep 15 '09 at 14:11
  • 32
    Why do you people vote for and answers which does not provide solution to the question? stringByTrimmingCharactersInSet does nor analyze the iside of the string but edges only. Answer by Georg Sholly is the perfect one. – Lukasz Nov 13 '11 at 21:45
  • 3
    Wasn't exactly an answer to the question, but it sure helped me. Thanks – daveMac Dec 11 '11 at 20:54
  • 1
    Excellent code for removing leading and trailing space at the same time. – user523234 Feb 12 '12 at 16:27
  • 1
    Helps a little but not a relevant answer for the question asked here. – Pawan Sharma Dec 05 '12 at 11:46
  • 1
    I dont know why people has voted this answer. Which is no where correct at all. – iOS_Developer Jun 10 '15 at 21:35
13

With a regex, but without the need for any external framework:

NSString *theString = @"    Hello      this  is a   long       string!   ";

theString = [theString stringByReplacingOccurrencesOfString:@" +" withString:@" "
                       options:NSRegularExpressionSearch
                       range:NSMakeRange(0, theString.length)];
MonsieurDart
  • 6,005
  • 2
  • 43
  • 45
  • You'd also then still need to trim the result, or you'll be padded with whitespace. This is probably the simplest answer, though. – lilbyrdie Jun 23 '11 at 15:28
  • 2
    the documentation for `NSRegularExpressionSearch` says that it only works with the `rangeOfString:...` methods – user102008 Jul 05 '11 at 19:56
9

A one line solution:

NSString *whitespaceString = @" String with whitespaces ";

NSString *trimmedString = [whitespaceString
        stringByReplacingOccurrencesOfString:@" " withString:@""];
sarnold
  • 102,305
  • 22
  • 181
  • 238
TwoBeerGuy
  • 631
  • 6
  • 3
  • 2
    Helped me out :). Thanks for that! – thedom Dec 20 '10 at 18:06
  • 5
    While this is useful, it removes all whitespace. The OP basically wants whitespace compaction, e.g. a trim followed by reduction of consecutive whitespace to a single whitespace. – lilbyrdie Jun 23 '11 at 15:27
  • Another note, this solution does no deal with tabs or newlines or whitespace characters other than spaces. – cthulhu Feb 08 '12 at 10:33
  • 2
    This does not answer the OP, but instead removes all the spaces in the string, so you end up with @"Stringwithwhitespaces" – charles Sep 17 '12 at 15:10
6

This should do it...

NSString *s = @"this is    a  string    with lots  of     white space";
NSArray *comps = [s componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

NSMutableArray *words = [NSMutableArray array];
for(NSString *comp in comps) {
  if([comp length] > 1)) {
    [words addObject:comp];
  }
}

NSString *result = [words componentsJoinedByString:@" "];
Barry Wark
  • 107,306
  • 24
  • 181
  • 206
4

Another option for regex is RegexKitLite, which is very easy to embed in an iPhone project:

[theString stringByReplacingOccurencesOfRegex:@" +" withString:@" "];
Daniel Dickison
  • 21,832
  • 13
  • 69
  • 89
3

Here's a snippet from an NSString extension, where "self" is the NSString instance. It can be used to collapse contiguous whitespace into a single space by passing in [NSCharacterSet whitespaceAndNewlineCharacterSet] and ' ' to the two arguments.

- (NSString *) stringCollapsingCharacterSet: (NSCharacterSet *) characterSet toCharacter: (unichar) ch {
int fullLength = [self length];
int length = 0;
unichar *newString = malloc(sizeof(unichar) * (fullLength + 1));

BOOL isInCharset = NO;
for (int i = 0; i < fullLength; i++) {
    unichar thisChar = [self characterAtIndex: i];

    if ([characterSet characterIsMember: thisChar]) {
        isInCharset = YES;
    }
    else {
        if (isInCharset) {
            newString[length++] = ch;
        }

        newString[length++] = thisChar;
        isInCharset = NO;
    }
}

newString[length] = '\0';

NSString *result = [NSString stringWithCharacters: newString length: length];

free(newString);

return result;
}
Divyu
  • 1,325
  • 9
  • 21
dmercredi
  • 2,112
  • 11
  • 10
3

Try This

NSString *theString = @"    Hello      this  is a   long       string!   ";

while ([theString rangeOfString:@"  "].location != NSNotFound) {
    theString = [theString stringByReplacingOccurrencesOfString:@"  " withString:@" "];
}
sinh99
  • 3,909
  • 32
  • 32
-1

Following two regular expressions would work depending on the requirements

  1. @" +" for matching white spaces and tabs
  2. @"\\s{2,}" for matching white spaces, tabs and line breaks

Then apply nsstring's instance method stringByReplacingOccurrencesOfString:withString:options:range: to replace them with a single white space.

e.g.

[string stringByReplacingOccurrencesOfString:regex withString:@" " options:NSRegularExpressionSearch range:NSMakeRange(0, [string length])];

Note: I did not use 'RegexKitLite' library for the above functionality for iOS 5.x and above.

Binarian
  • 12,296
  • 8
  • 53
  • 84
apalvai
  • 587
  • 5
  • 11
  • This solution doesn't remove leading and trailing whitespace as the OP asks for. – hfossli Dec 03 '14 at 08:22
  • @hfossli leading/trailing spaces can be removed by directly calling NSString's stringByTrimmingCharactersInSet: method with new/white line characterset. Above solution was to remove the redundant spaces independent of their location. – apalvai Dec 12 '14 at 22:49
-1

You can also use a simple while argument. There is no RegEx magic in there, so maybe it is easier to understand and alter in the future:

while([yourNSStringObject replaceOccurrencesOfString:@"  "
                         withString:@" "
                         options:0
                         range:NSMakeRange(0, [yourNSStringObject length])] > 0);
-1

Alternative solution: get yourself a copy of OgreKit (the Cocoa regular expressions library).

  • OgreKit (Japanese webpage -- code is in English)
  • OgreKit (Google autotranslation):

The whole function is then:

NSString *theStringTrimmed =
   [theString stringByTrimmingCharactersInSet:
        [NSCharacterSet whitespaceAndNewlineCharacterSet]];
OGRegularExpression  *regex =
    [OGRegularExpression regularExpressionWithString:@"\s+"];
return [regex replaceAllMatchesInString:theStringTrimmed withString:@" "]);

Short and sweet.

If you're after the fastest solution, a carefully constructed series of instructions using NSScanner would probably work best but that'd only be necessary if you plan to process huge (many megabytes) blocks of text.

Matt Gallagher
  • 14,858
  • 2
  • 41
  • 43
  • Is there a reason to use OgreKit instead of RegExKitLite? http://regexkit.sourceforge.net It has a very similar replaceOccurrencesOfRegex call, and works on top of the existing RegEX libraries (not sure if Ogre is a whole RegEX engine or what) – Kendall Helmstetter Gelner Apr 18 '09 at 04:42
  • I'm sure both will work. I haven't used regexkit but its a good suggestion to make. People should choose based on the underlying libraries: the PERL-compatible pcre (RegExKitLite) and the Ruby-compatible Oniguruma (OgreKit). – Matt Gallagher Apr 20 '09 at 02:50
-1

according from @Mathieu Godart is best answer, but some line is missing , all answers just reduce space between words , but when if have tabs or have tab in place space , like this: " this is text \t , and\tTab between , so on " in three line code we will : the string we want reduce white spaces

NSString * str_aLine = @"    this is text \t , and\tTab between      , so on    ";
// replace tabs to space
str_aLine = [str_aLine stringByReplacingOccurrencesOfString:@"\t" withString:@" "];
// reduce spaces to one space
str_aLine = [str_aLine stringByReplacingOccurrencesOfString:@" +" withString:@" "
                                                    options:NSRegularExpressionSearch
                                                      range:NSMakeRange(0, str_aLine.length)];
// trim begin and end from white spaces
str_aLine = [str_aLine stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

the result is

"this is text , and Tab between , so on"

without replacing tab the resul will be:

"this is text    , and  Tab between , so on"
Kosar
  • 170
  • 2
  • 13