Regexp not working

Hi everybody,

I had a macro that was working on one Mac, and then when I synced it with a new Mac it doesn’t work anymore. What I want to do is to copy some text from Microsoft Todo (the notes section of a task) and create variables from that text which I then use to search in Outlook.

Here is an example of the text:

To: servicedesk@somecompany.com
Subject: Parking space
Time: 2020-11-03T14:18:39+00:00
---------------------------------------------
Hi!

I'd like to reserve a parking spot.

I use this regexp expression which works fine on https://regex101.com:
\A(.+)(\n+)(.+)(\n+)([\s\S]+)

I've also created the following config in Keyboard Maestro (the clipboard contains the text):

My problem is that this only works when I copy the text from for example TextEdit, whenever I copy the text from Microsoft ToDo the capture groups are not put into variables.

I try to display the system clipboard in case the text was cached on not copied correctly, but the window shows the copied text.

I'm at my wits end, what am I doing wrong?

This may just take us back to the Jamie Zawinski quote:

[Now they have two problems](https://www.goodreads.com/author/quotes/7549076.Jamie_Zawinski)

The cost / benefit of regular expressions is often doubtful, and as they grow, they quickly approach a write-only condition.

It might be more productive (quicker to test and debug) to:

  1. use an Execute JavaScript action
  2. Split into lines ( String.split )
  3. Find the lines that start with particular strings ( String.startsWith )

(remember that line-endings coming in from Microsoft software may not be LF delimited)

1 Like

First, I'd encourage you to continue learning RegEx. IMO it is one of the most powerful and useful languages you can learn. You can use it just about everywhere. If you want more info on RegEx, just ask.

I'm not sure why this works for you in TextEdit and not MS ToDo, but it could be the newline character (CR vs LF).
Sometimes it is easier and more reliable to use a separate RegEx Search for each variable. And that is what I have done in this example Macro.

Example Results

image

Below is just an example written in response to your request. You will need to use as an example and/or change to meet your workflow automation needs.

Please let us know if it meets your needs.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MACRO:   Extract Data From String Using RegEx [Example]

-~~~ VER: 1.0    2020-11-03 ~~~
Requires: KM 8.2.4+   macOS 10.11 (El Capitan)+
(Macro was written & tested using KM 9.0+ on macOS 10.14.5 (Mojave))

DOWNLOAD Macro File:

Extract Data From String Using RegEx [Example].kmmacros
Note: This Macro was uploaded in a DISABLED state. You must enable before it can be triggered.


1 Like

OK, that source does indeed delimit lines on Windows-style '\r\n', rather than the '\n' assumed in your regular expression patterns,
and it seems that all you are looking for is the Subject: line, and the value following that prefix.

So I would personally:

  • clamber out of the regex tar-pit
  • split the lines on \r\n
  • find the the first line that starts with 'Subject: ', and take the rest of it.

You could do this in AppleScript or JS.

Here, FWIW, is a JS version, with source code, for pasting into a KM Execute JXA action, below:

image

Copy all of this and paste into the Execute JavaScript for Automation action:

(() => {
    'use strict';

    ObjC.import('AppKit');

    const main = () => {
        const
            xs = lines(clipboardText()),
            subjectLine = xs.find(
                x => x.startsWith('Subject: ')
            );

        return undefined !== subjectLine ? (
            subjectLine.split(': ')[1]
        ) : 'Subject not found ...'
    };

    // ----------------------- JXA -----------------------

    // clipboardText :: IO () -> String
    const clipboardText = () =>
        // Any plain text in the clipboard.
        ObjC.unwrap(
            $.NSString.alloc.initWithDataEncoding(
                $.NSPasteboard.generalPasteboard
                .dataForType($.NSPasteboardTypeString),
                $.NSUTF8StringEncoding
            )
        );

    // lines :: String -> [String]
    const lines = s =>
        // A list of strings derived from a single
        // string delimited by newline and or CR.
        0 < s.length ? (
            s.split(/[\r\n]+/)
        ) : [];

    return main();
})();

Thanks! Marked this as a solution, it works beautifully!

1 Like

As a footnote, although I personally reach first for JS to define something like:

The first line that starts with "Subject:"

we can readily define the same thing directly in terms of the building blocks that @peternlewis provides in Keyboard Maestro.

In particular, I notice that:

  • when the For Each action works through the lines of a variable or clipboard, it is not bamboozled by that difference between Windows-style and macOS or Unix-style endings which you have to get right in a regular expression.

Others will be better-practiced in the choice of these actions, but this kind of thing seems to define exactly the same pattern as the JavaScript:

1 Like

I like this method! But I'm not able to replicate it; the window that should contain the variable subjectValue is empty. Not sure if I perhaps have the action Get Substring of Variable "subjectText" misconfigured, perhaps you can expand that section?

Also, I'm not sure I completely follow the logic: where is lineText defined in the condition "For each lineText in these collections"? Shouldn't I need to first put each line in clipText into the variable lineText, and then check it?

And the variable subjectValue is not defined anywhere except for in the display text action at the end, where is this value populated?

That's right, it's not a complete macro, just an illustration of a method.

You could get the MS Todo note into the clipboard as before, and then specify the For Each collection as the lines of the System Clipboard:

where is lineText defined in the condition "For each lineText in these collections"?

The For Each action does all that work for you – bringing each line, one by one, into the lineText variable.

Thanks again for your help, I was able to make the following work:

Note that the "delete to" option at the end of the loop is used to remove the "Subject:" prefix if needed, I use it since the old and new Outlook for Mac works differently with regards to search parameters.

1 Like