From e4e7f5a71aa985318e99a62c607ac4c932aa0e8e Mon Sep 17 00:00:00 2001 From: achmizs <1874748+achmizs@users.noreply.github.com> Date: Sat, 4 Dec 2021 14:59:54 -0500 Subject: [PATCH] Updated whitesplace-splitting methods --- NSString+SA_NSStringExtensions.h | 7 +- NSString+SA_NSStringExtensions.m | 143 ++++++++++++++++++++----------- 2 files changed, 96 insertions(+), 54 deletions(-) diff --git a/NSString+SA_NSStringExtensions.h b/NSString+SA_NSStringExtensions.h index 70e2781..ef747cc 100644 --- a/NSString+SA_NSStringExtensions.h +++ b/NSString+SA_NSStringExtensions.h @@ -36,14 +36,15 @@ #pragma mark - Trimming /**********************/ -/* “Smart” trimming. - */ typedef NS_OPTIONS(NSUInteger, SA_NSStringTrimmingOptions) { SA_NSStringTrimming_CollapseWhitespace = 1 << 0, SA_NSStringTrimming_TrimWhitespace = 1 << 1, SA_NSStringTrimming_AppendEllipsis = 1 << 2, SA_NSStringTrimming_ElideEllipsisWhenEmpty = 1 << 3 }; + +/* “Smart” trimming. + */ -(NSString *) stringByTrimmingToMaxLengthInBytes:(NSUInteger)maxLengthInBytes usingEncoding:(NSStringEncoding)encoding withStringEnumerationOptions:(NSStringEnumerationOptions)enumerationOptions @@ -118,6 +119,8 @@ typedef NS_OPTIONS(NSUInteger, SA_NSStringTrimmingOptions) { */ -(NSArray *) componentsSplitByWhitespace; -(NSArray *) componentsSplitByWhitespaceWithMaxSplits:(NSUInteger)maxSplits; +-(NSArray *) componentsSplitByWhitespaceWithMaxSplits:(NSUInteger)maxSplits + dropEmptyString:(BOOL)dropEmptyString; /* Splitting by specified delimiter string. */ diff --git a/NSString+SA_NSStringExtensions.m b/NSString+SA_NSStringExtensions.m index 6a51885..dd5d6b0 100644 --- a/NSString+SA_NSStringExtensions.m +++ b/NSString+SA_NSStringExtensions.m @@ -332,66 +332,105 @@ static BOOL _SA_NSStringExtensions_RaiseRegularExpressionCreateException = YES; /***********************/ -(NSArray *) componentsSplitByWhitespace { - return [self componentsSplitByWhitespaceWithMaxSplits:NSUIntegerMax]; + return [self componentsSplitByWhitespaceWithMaxSplits:NSUIntegerMax + dropEmptyString:YES]; } -(NSArray *) componentsSplitByWhitespaceWithMaxSplits:(NSUInteger)maxSplits { - if (maxSplits == 0) { + return [self componentsSplitByWhitespaceWithMaxSplits:maxSplits + dropEmptyString:YES]; +} + +-(NSArray *) componentsSplitByWhitespaceWithMaxSplits:(NSUInteger)maxSplits + dropEmptyString:(BOOL)dropEmptyString { + // No need to do anything fancy in this case. + if (maxSplits == 0) return @[ self ]; - } - + + static NSRegularExpression *regexp; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + regexp = [@"(?:^|\\S|$)+" regularExpression]; + }); + NSMutableArray *components = [NSMutableArray array]; - - __block NSUInteger tokenStart; - __block NSUInteger tokenEnd; - __block BOOL currentlyInToken = NO; - __block NSRange tokenRange = NSMakeRange(NSNotFound, 0); - - __block NSUInteger splits = 0; - - [self enumerateSubstringsInRange:self.fullRange - options:NSStringEnumerationByComposedCharacterSequences - usingBlock:^(NSString *character, - NSRange characterRange, - NSRange enclosingRange, - BOOL *stop) { - if ( currentlyInToken == NO - && [character containsCharactersInSet:[[NSCharacterSet whitespaceCharacterSet] invertedSet]] - ) { - currentlyInToken = YES; - tokenStart = characterRange.location; - } else if ( currentlyInToken == YES - && [character containsCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] - ) { - currentlyInToken = NO; - tokenEnd = characterRange.location; - - tokenRange = NSMakeRange(tokenStart, tokenEnd - tokenStart); - [components addObject:[self substringWithRange:tokenRange]]; - splits++; - if (splits == maxSplits) { - *stop = YES; - NSRange lastTokenRange = [self rangeToEndFrom:[self firstNonWhitespaceAfterRange:tokenRange]]; - if (lastTokenRange.location != NSNotFound) { - [components addObject:[self substringWithRange:lastTokenRange]]; - } - } - } - }]; - - // If we were in a token when we got to the end, add that last token. - if ( splits < maxSplits - && currentlyInToken == YES) { - tokenEnd = self.length; - - tokenRange = NSMakeRange(tokenStart, - tokenEnd - tokenStart); - [components addObject:[self substringWithRange:tokenRange]]; - } - + [regexp enumerateMatchesInString:self + options:(NSMatchingOptions) 0 + range:self.fullRange + usingBlock:^(NSTextCheckingResult * _Nullable result, + NSMatchingFlags flags, + BOOL * _Nonnull stop) { + if ( dropEmptyString + && result.range.length == 0) { + // Nothing. + } else if (components.count < maxSplits) { + [components addObject:[self substringWithRange:result.range]]; + } else { + [components addObject:[self substringWithRange:[self rangeToEndFrom:result.range]]]; + *stop = YES; + } + }]; return components; } +//-(NSArray *) componentsSplitByWhitespaceWithMaxSplits:(NSUInteger)maxSplits { +// if (maxSplits == 0) { +// return @[ self ]; +// } +// +// NSMutableArray *components = [NSMutableArray array]; +// +// __block NSUInteger tokenStart; +// __block NSUInteger tokenEnd; +// __block BOOL currentlyInToken = NO; +// __block NSRange tokenRange = NSMakeRange(NSNotFound, 0); +// +// __block NSUInteger splits = 0; +// +// [self enumerateSubstringsInRange:self.fullRange +// options:NSStringEnumerationByComposedCharacterSequences +// usingBlock:^(NSString *character, +// NSRange characterRange, +// NSRange enclosingRange, +// BOOL *stop) { +// if ( currentlyInToken == NO +// && [character containsCharactersInSet:[[NSCharacterSet whitespaceCharacterSet] invertedSet]] +// ) { +// currentlyInToken = YES; +// tokenStart = characterRange.location; +// } else if ( currentlyInToken == YES +// && [character containsCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] +// ) { +// currentlyInToken = NO; +// tokenEnd = characterRange.location; +// +// tokenRange = NSMakeRange(tokenStart, +// tokenEnd - tokenStart); +// [components addObject:[self substringWithRange:tokenRange]]; +// splits++; +// if (splits == maxSplits) { +// *stop = YES; +// NSRange lastTokenRange = [self rangeToEndFrom:[self firstNonWhitespaceAfterRange:tokenRange]]; +// if (lastTokenRange.location != NSNotFound) { +// [components addObject:[self substringWithRange:lastTokenRange]]; +// } +// } +// } +// }]; +// +// // If we were in a token when we got to the end, add that last token. +// if ( splits < maxSplits +// && currentlyInToken == YES) { +// tokenEnd = self.length; +// +// tokenRange = NSMakeRange(tokenStart, +// tokenEnd - tokenStart); +// [components addObject:[self substringWithRange:tokenRange]]; +// } +// +// return components; +//} + -(NSArray *) componentsSeparatedByString:(NSString *)separator maxSplits:(NSUInteger)maxSplits { NSArray *components = [self componentsSeparatedByString:separator];