Last active
September 25, 2021 02:11
-
-
Save holtwick/87a48551ac4e6326925c57f7a1945b5c to your computer and use it in GitHub Desktop.
ObjC RegExp Helper with rudimentary named groups
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2012 Dirk Holtwick, holtwick.it. All rights reserved. | |
@import Foundation; | |
#define HORegexDefaultOptions NSRegularExpressionCaseInsensitive | NSRegularExpressionAnchorsMatchLines | NSRegularExpressionAllowCommentsAndWhitespace | |
@interface HORegexMatch : NSTextCheckingResult | |
@property (readonly, nonatomic) NSString *text; | |
@property (readonly, nonatomic) NSTextCheckingResult *match; | |
- (NSString *)stringForGroupName:(NSString *)name; | |
@property (readonly, copy) NSString *stringValue; | |
- (id)objectForKeyedSubscript:(id)key; | |
@end | |
/// | |
@interface HORegex : NSObject | |
@property (readonly, nonatomic) NSRegularExpression *regex; | |
@property (readonly, nonatomic) NSDictionary *groupNameMappings; | |
- (instancetype)initWithPattern:(NSString *)pattern options:(NSRegularExpressionOptions)options; | |
- (instancetype)initWithPattern:(NSString *)pattern; | |
+ (HORegex *)regexWithPattern:(NSString *)pattern options:(NSRegularExpressionOptions)options cache:(BOOL)cache; | |
- (NSArray <NSString *> *)allStringMatchesinString:(NSString *)string; | |
- (NSArray <HORegexMatch *> *)allRegexgMatchesinString:(NSString *)string; | |
- (void)enumerateMatchesInString:(NSString *)string options:(NSMatchingOptions)options usingBlock:(void (^)(HORegexMatch *result, BOOL *stop))block; | |
- (void)enumerateMatchesInString:(NSString *)string usingBlock:(void (^)(HORegexMatch *result, BOOL *stop))block; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2012 Dirk Holtwick, holtwick.it. All rights reserved. | |
#import "HORegex.h" | |
@implementation HORegexMatch { | |
HORegex *_regex; | |
NSString *_text; | |
NSTextCheckingResult *_match; | |
} | |
- (instancetype)initWithRegex:(HORegex *)regex text:(NSString *)text textCheckingResult:(NSTextCheckingResult *)match { | |
self = [super init]; | |
if (self) { | |
_regex = regex; | |
_text = text; | |
_match = match; | |
} | |
return self; | |
} | |
- (NSTextCheckingResult *)match { | |
return _match; | |
} | |
- (NSString *)text { | |
return _text; | |
} | |
- (NSString *)stringForGroupName:(NSString *)name { | |
NSNumber *offset = _regex.groupNameMappings[name]; | |
// hxAssert(offset != nil, nil); | |
if(offset == nil) { | |
return nil; | |
} | |
return [_match inString:_text at:offset.integerValue]; | |
} | |
- (id)objectForKeyedSubscript:(id)key { | |
return [self stringForGroupName:key]; | |
} | |
- (NSString *)stringValue { | |
return [_text substringWithRange:_match.range]; | |
} | |
@end | |
@implementation HORegex { | |
NSString *_pattern; | |
NSRegularExpressionOptions _options; | |
NSMutableDictionary *_groupNameMappings; | |
NSRegularExpression *_regex; | |
} | |
#pragma mark - Static | |
static NSRegularExpression *sRXFindNamedGroups; | |
static NSRegularExpression *sRXCountGroups; | |
static NSCache *sRegexCache; | |
+ (void)initialize { | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
sRXFindNamedGroups = [NSRegularExpression regularExpressionWithPattern:@"\\(\\?\\<([\\w\\d\\-]+)\\>" options:0 error:NULL]; | |
sRXCountGroups = [NSRegularExpression regularExpressionWithPattern:@"\\((?!\\?([\\:\\#\\=\\!]|\\<\\!|\\<\\=))" options:0 error:NULL]; | |
sRegexCache = [[NSCache alloc] init]; | |
sRegexCache.name = NSStringFromClass(self); | |
}); | |
} | |
#pragma mark - Named Groups | |
- (void)setLabel:(NSString *)label offset:(NSInteger)offset { | |
if(!_groupNameMappings) { | |
_groupNameMappings = [NSMutableDictionary dictionary]; | |
} | |
_groupNameMappings[label] = @(offset + 1); | |
} | |
- (NSDictionary *)groupNameMappings { | |
return _groupNameMappings; | |
} | |
#pragma mark - Shortcuts | |
- (NSArray <NSString *> *)allStringMatchesinString:(NSString *)string { | |
return [[self.regex matchesInString:string options:0 range:string.range] hoMap:^id(id obj) { | |
NSTextCheckingResult *match = (id)obj; | |
return [string substringWithRange:match.range]; | |
}]; | |
} | |
- (NSArray <HORegexMatch *> *)allRegexgMatchesinString:(NSString *)string { | |
return [[self.regex matchesInString:string options:0 range:string.range] hoMap:^id(id obj) { | |
NSTextCheckingResult *match = (id)obj; | |
return [[HORegexMatch alloc] initWithRegex:self text:string textCheckingResult:match]; | |
}]; | |
} | |
#pragma mark - Enumeration | |
- (void)enumerateMatchesInString:(NSString *)string options:(NSMatchingOptions)options usingBlock:(void (^)(HORegexMatch *result, BOOL *stop))block { | |
[_regex enumerateMatchesInString:string options:options range:string.range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { | |
block([[HORegexMatch alloc] initWithRegex:self text:string textCheckingResult:result], stop); | |
}]; | |
} | |
- (void)enumerateMatchesInString:(NSString *)string usingBlock:(void (^)(HORegexMatch *result, BOOL *stop))block { | |
[self enumerateMatchesInString:string options:0 usingBlock:block]; | |
} | |
/// Final NSRegularExpression | |
- (NSRegularExpression *)regex { | |
return _regex; | |
} | |
#pragma mark - Factory | |
+ (HORegex *)regexWithPattern:(NSString *)pattern options:(NSRegularExpressionOptions)options cache:(BOOL)cache { | |
HORegex *regex; | |
NSString *key = [NSString stringWithFormat:@"%@/%lu", pattern, (unsigned long)options]; | |
if(cache) { | |
regex = [sRegexCache objectForKey:key]; | |
if (regex) { | |
return regex; | |
} | |
} | |
regex = [[HORegex alloc] initWithPattern:pattern options:options]; | |
if(cache && regex) { | |
[sRegexCache setObject:regex forKey:key]; | |
} | |
return regex; | |
} | |
#pragma mark - Setup | |
- (void)configure { | |
[sRXFindNamedGroups enumerateMatchesInString:_pattern options:0 range:_pattern.range usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) { | |
NSString *label = [match inString:self->_pattern at:1]; | |
hxAssert(!self->_groupNameMappings[label], @"Only define once!"); | |
NSInteger offset = | |
[sRXCountGroups numberOfMatchesInString:self->_pattern | |
options:0 | |
range:NSMakeRange(0, match.range.location)]; | |
[self setLabel:label offset:offset]; | |
}]; | |
NSString *newPattern = _pattern; | |
for(NSString *label in _groupNameMappings.allKeys) { | |
newPattern = [newPattern replace:[NSString stringWithFormat:@"(?<%@>", label] with:@"("]; | |
} | |
NSError *error = nil; | |
_regex = [[NSRegularExpression alloc] initWithPattern:newPattern | |
options:_options | |
error:&error]; | |
if(!_regex) { | |
XLogObject(error); | |
} | |
hxAssert0(_regex); | |
} | |
- (instancetype)initWithPattern:(NSString *)pattern options:(NSRegularExpressionOptions)options { | |
self = [super init]; | |
if (self) { | |
_pattern = pattern; | |
_options = options; | |
[self configure]; | |
} | |
return self; | |
} | |
- (instancetype)initWithPattern:(NSString *)pattern { | |
return [self initWithPattern:pattern options:HORegexDefaultOptions]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment