Cocoa Regular Expressions via JavascriptCore

UPDATE check out JSCocoa for more JavascriptCore sample code.

Ever been stumped by the lack of regular expressions in Cocoa ? You can do quite a lot of complicated stuff in a few lines of code but comes the time to match a string to some pattern and then … where are regexes ? On your mac, they're living in PHP, Ruby, Perl, on webpages via WebKit, in some command line tools, but where in Cocoa ? Nowhere ! How about using WebKit ? Why not, but WebKit needs a NSWindow, a WebView, an html page with some code. Someone on CocoaDev suggested JavascriptCore, so we'll use just that !

Using JavascriptCore

To use JavascriptCore and get some results out of it :

  • create a context : JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
  • create a JS script string : JSStringRef scriptJS = JSStringCreateWithCFString((CFStringRef)@"var p = new RegExp(pattern, flags); string.match(p)");
  • evaluate our script and get the result : JSValueRef result = JSEvaluateScript(ctx, scriptJS, NULL, NULL, 0, NULL);
  • convert our Javascript result to a Cocoa string :
JSStringRef resultStringJS = JSValueToStringCopy(ctx, result, NULL);
CFStringRef resultString = JSStringCopyCFString(kCFAllocatorDefault, resultStringJS);
// Returns a Cocoa object by casting from a CFString
return	(id)resultString;

There's more as you have to do conversions to and from Cocoa. WebKit bridges strings, arrays, numbers but JavascriptCore does nothing, so we have to handle it ourselves.

From there we do a direct mapping of Javascript's match and replace.

Augmenting NSString

We'll define three methods :

  • match matches the first result found. Returns a string or nil. Equivalent to Javascript's string.match(/pattern/)
  • matchAll returns all matches. Returns a string array or nil. Equivalent to Javascript's string.match(/pattern/g)
  • replace matches the first result found. Returns a string or nil. Equivalent to Javascript's string.replace(/pattern/)

Given a sample string That is a sample STRING and a pattern of I[\w]+ (match capital I followed by one or more [a-zA-Z0-9]) :

// matches ING
- (id)matchWithPattern:(NSString*)pattern;
// matches ['ING']
- (id)matchAllWithPattern:(NSString*)pattern;
// returns That is a sample STR***
- (id)replaceWithString:(NSString*)replacement andPattern:(NSString*)pattern;

And case (in)sensitive versions :

// matches is
- (id)matchWithPattern:(NSString*)pattern isCaseSensitive:(BOOL)c;
// matches ['is', 'ING']
- (id)matchAllWithPattern:(NSString*)pattern isCaseSensitive:(BOOL)c;
// returns That *** a sample STR***
- (id)replaceWithString:(NSString*)replacement andPattern:(NSString*)pattern isCaseSensitive:(BOOL)c;

Sample code

Image:iconZip.png RegEx via NOTE this uses JavascriptCore for regular expressions, and WebView to display the results. That means there are actually two Javascript engines running :) one for matching, one for displaying. The displaying is of course completely optional, if you want to use regexes in your project copy RegExJS.m and RegExJS.h then add JavascriptCore.framework.

Tony Arnold
2011 03 16

I used this approach in a recent release of my app, and I have to say — whilst it's quite clever, it blows out memory usage significantly due to the JS JIT compiler and other things being hosted in your app's memory space. If you don't have concerns about memory, this is a nice, simple approach — otherwise, I think you're better off finding something built on top of the simple POSIX regex tools.

Follow me on Twitter
Planet Cocoa

2011 02 22Distance field
2010 07 202Binding through NSApp
2010 05 122Forwarding invocations
2010 02 272Core Image black fringes
2010 02 21Quickest Way to Shell
2010 02 08Who's calling ?
2009 09 2138 ways to use Blocks in Snow Leopard
2009 08 182Bracket Mess
2009 08 124Taming JavascriptCore within and without WebView
2009 04 15Debugging with Activity Monitor
2009 03 25How Core Image Color Tracking works
2009 03 1510Custom NSThemeFrame
2009 03 10Which framework is running ?
2009 03 074CoreUI can paint pretty big
2009 02 18Localization with functions
2009 01 30Did you forget to nest alloc and init?
2009 01 16JSCocoa on the iPhone
2009 01 11Mixing WebView and JavascriptCore
2009 01 09Badge overflow
2009 01 09Find your Garbage Collection leaks with Instruments

Powered by MediaWiki