4

I have the following function:

@testVisible private String selectBeforeKeyword (String remainingStepsAsString, String keyword)
{
    List<String> splitList = remainingStepsAsString.split('\\b'+keyword+'\\b');

    return (splitList.size() > 1)
                ? (splitList[0].trim())
                : null;
}

This works as I need it to. Yet I am not really looking to split the the string, but rather to capture the beginning of it. So, I'm thinking a more appropriate solution might be something like this:

@testVisible private String selectBeforeKeyword (String remainingStepsAsString, String keyword)
{
    Pattern keywordPattern = Pattern.compile('(.*)\\b'+keyword+'\\b');
    Matcher keywordMatcher = keywordPattern.matcher(remainingStepsAsString);

        return (keywordMatcher.matches())
                ? (keywordMatcher.group(0).trim())
                : null;
}

... however, this doesn't work, returning null instead.

What would the correct pattern to compile to make this work?

8
  • If you are getting the Pattern/Matcher solution to work, it would be interesting to see, how its performance compares to split(). Personally i would have used substringBefore(String). Oct 7, 2014 at 9:19
  • @AlexanderJohannes, I thought of using substringBefore, but I'm not sure that supports regex and I'm concerned with the whitespace before and after the keyword, which I don't want to assume will be spaces (e.g. it could be tabs or linefeeds). Oct 7, 2014 at 9:44
  • Is there one backslash or two? If I'm not mistaken, each backslash needs to be escaped. If you simply use the backslashes for your regex pattern, you can use the indexOf to find their locations and capture the string between. Best RegEx pattern developer site I know of is gskinner.com. Has a nice testing utility to help you create & test them. Just don't rely on using the universal attributes since they don't apply to SF.
    – crmprogdev
    Oct 7, 2014 at 11:57
  • @crmprogdev, Yes, it is also my understanding with SFDC you need to escape the backslashes, but I think I tried it both ways without success. I've seen a few sites which attempt to help with regex, but none is SFDC specific, so none deal with SFDC's quirks (like needing to escape the slashes). Oct 7, 2014 at 14:37
  • Once you get the pattern you need, you can add any additional escapes. Gskinner's site does need escapes for backslashes (and others as documented in the reference). It has worked well for me in the past (note correct link provided). The main thing is that you can't use any of the flags, particularly the ignore case flag, with SF. The global flag is irrelevant as is the multiline in your situation.
    – crmprogdev
    Oct 7, 2014 at 14:55

1 Answer 1

1

I was curious to see whether @Alexander was right, so I set up a simpler pattern matcher, and compared its runtime to the string split method. Each method was executed 1,000 times per run:

String keyword = 'World ';
String myString = 'Hello World.';

Integer stringSplitBegin; 
Integer stringSplitEnd;
Integer patternMatchBegin;
Integer patternMatchEnd;

stringSplitBegin = DateTime.now().millisecond();
for(Integer i=0; i < 1000; i++){
    String keyword = 'World';
    String myString = 'Hello World.';
    List<String> splitList = myString.split('\\b'+keyword+'\\b');

}
stringSplitEnd = DateTime.now().millisecond();


patternMatchBegin = DateTime.now().millisecond();
for(Integer i=0; i < 1000; i++){
    Pattern keywordPattern = Pattern.compile(keyword);
    Matcher keywordMatcher = keywordPattern.matcher(myString);
    
}
patternMatchEnd = DateTime.now().millisecond();

system.debug('stringSplit:  ' + (stringSplitEnd - stringSplitBegin) + 'ms');
system.debug('patternMatch: ' + (patternMatchEnd - patternMatchBegin) + 'ms');

I ran the code 5 times as anonymous Apex, and these were the results:

|DEBUG|stringSplit: 13ms :: patternMatch: 60ms

|DEBUG|stringSplit: 10ms :: patternMatch: 51ms

|DEBUG|stringSplit: 10ms :: patternMatch: 41ms

|DEBUG|stringSplit: 9ms :: patternMatch: 41ms

|DEBUG|stringSplit: 11ms :: patternMatch: 49ms

So as it works out, Alex was right. The string split method was consistently faster, by an average of 37.8ms.

I had been hoping to find a way to get the pattern matcher to work with a positive lookahead, but I don't think Apex supports them. No matter anyway, it seems!

Pattern keywordPattern = Pattern.compile('^.*(?=(' + keyword + '))');
3
  • Thanks for finally solving the "riddle". Forgot I asked this. Cheers for the additional speed comparison, as well. :-) Oct 7, 2018 at 16:00
  • No problem. @BrianKessler, do you know whether I'm right about Apex not supporting lookarounds? That's what I was trying to figure out when I found your question. Oct 7, 2018 at 16:05
  • No idea... I really haven't worked with regex that much. Oct 8, 2018 at 1:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .