0

I have the following String: "Trastuzumab IV (Herceptin): Request change to Trazimera or Monoferric"

I am using the following regex:

(?<!\()\b(Trazimera||Monoferric)\b(?![\w\s]*[\)])

I need to return a list of all the matches in order and retrieve the last match.

Is this possible with Salesforce's limited regex abilities?

1 Answer 1

2

Is this possible with Salesforce's limited regex abilities?

Salesforce's regular expressions are more powerful than people give it credit for. It's just misunderstood because it's different than JavaScript's and Perl's implementations where RegExp has built-in syntax. Java developers would be right at home with Salesforce's regular expressions, as it draws inspiration from Java's Pattern implementation.


In Apex, regular expressions use \ for certain sequences, just as every other language that has regular expressions. However, in Apex, \ is also an escape sequence, so, for example, \b is BEL (0x07) inside of a string, while \\b means \b as far as the regular expression engine is concerned.

Fun fact: BEL is called BEL because in really old computer systems, "printing" a BEL to the screen would cause the computer to beep, which also froze the entire system (including additional output) until the beep finished, which was from an even older technology (teletypewriters), that literally rang a bell when it received this character, in order to get an operator's attention.

So, to get the expected result, you need to double-up on those backslashes. Also, the || bit makes it match, well, nothing, so you get spurious matches. It should be just |:

(?<!\\()\\b(Trazimera|Monoferric)\\b(?![\\w\\s]*[\\)])

Now, as far as getting a list of values, you just need to iterate through each find and group set:

Pattern p = Pattern.compile('(?<!\\()\\b(Trazimera|Monoferric)\\b(?![\\w\\s]*[\\)])');
String s = 'Trastuzumab IV (Herceptin): Request change to Trazimera or Monoferric';
Matcher m = p.matcher(s);
while(m.find()) {
    System.debug('Match found: '+m.group(0));
    Integer groupCount = m.groupCount() + 1; // 0 is the full match
    for(Integer index = 1; index < groupCount; index++) {
        System.debug(m.group(index));
    }
}

Output:

[5]|DEBUG|Match found: Trazimera
[8]|DEBUG|Trazimera
[5]|DEBUG|Match found: Monoferric
[8]|DEBUG|Monoferric

As you can see, it works just as specified. However, in case it's still not doing what you want, I'd suggest it's your regular expression that's wrong, not Apex.

As far as getting the full list, just add the values to a List. The last element in the List is the last found element.

9
  • Why would m.group() have an index? Wouldnt it always be m.group(0)? Apr 14, 2022 at 22:20
  • 1
    @MatthewMetros m.group() is an alias for m.group(0). This is the "full" match that the pattern matched. From there, m.group(1) and so on are the "capture groups", designated by (...). These groups allow you to extract parts out from the overall pattern that you're interested in. You can use this when you want to match a bigger pattern, but only need some smaller subpattern within.
    – sfdcfox
    Apr 14, 2022 at 22:30
  • I get the following error: Unable to execute regular expression. java.util.regex.PatternSyntaxException: Unclosed group near index 54 (?<!\()\\b(Trazimera|Monoferric)\\b(?![\\w\\s]*[\)]) Apr 15, 2022 at 15:09
  • 1
    @MatthewMetros Check your backslashes. If this copy-paste is correct, you're missing two, one after the ! and one after the final [. The code I wrote in the answer runs correctly.
    – sfdcfox
    Apr 15, 2022 at 15:19
  • 1
    @MatthewMetros That error means you have escaped your backslashes when you should not have. Again, you double the backslashes in source code, but not in other places, such as a Custom Label.
    – sfdcfox
    Apr 15, 2022 at 15:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.