Skip to content

Instantly share code, notes, and snippets.

@holtwick
Last active September 25, 2021 02:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save holtwick/87a48551ac4e6326925c57f7a1945b5c to your computer and use it in GitHub Desktop.
Save holtwick/87a48551ac4e6326925c57f7a1945b5c to your computer and use it in GitHub Desktop.
ObjC RegExp Helper with rudimentary named groups
// 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
// 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