RegEx to parse MIME Message bodies in between. How?

Viewed 114

I'm writing an IMAP4 backup app. After much research I have found the correct IMAP command to return all messages or a range of messages.

SS01 UID FETCH 1:* BODY[]

This beautiful command returns data in to following format:

* 1 FETCH (UID 2 BODY[] {7765}
data to be extracted
from here! which can possibly contain
) <--- one or more prior to its final...
)
* 2 FETCH (UID 3 BODY[] {443}
data to be extracted
from here! which can possibly contain
) <--- one or more prior to its final...
)
* 3 FETCH (UID 4 BODY[] {4432}
data to be extracted
from here! which can possibly contain
) <--- one or more prior to its final...
)
* 4 FETCH (UID 5 BODY[] {123}
data to be extracted
from here! which can possibly contain
) <--- one or more prior to its final...
)
SS01 OK Success

The only unique patterns I am able to find in this text are:

The very first message begins with...

1 FETCH (UID 2 BODY[] {7765}

Each message that is not the last ends with....

)
* 2 FETCH (UID 3 BODY[] {443}

The last message ends with...

)
SS01 OK Success

I have found the following example on the site that I am trying to implement but with no success.

The RegEx pattern is:

(?<=This is)(.*)(?=sentence)

Here's is a minimum reproducible example that doesn't work.

(\*\s\d+\s\w+\s\(UID\s\d+\sBODY\[\]\s\{\d+\})(.*\n)(\)\n\*\s\d+\s\w+\s\(UID\s\d+\sBODY\[\]\s\{\d+\})
2 Answers

You can greatly simplify your regex like so:

\{\d+\}$[\r\n]+([\s\S]+?)^\)$
  • \{\d+\}$ - find {digits} at the end of a line
  • [\r\n]+ - capture any new lines
  • ([\s\S]+?) - loosely capture the desired text leading up to: (read the following bullet point)
  • ^\)$ - find a line with nothing more than a closing parenthesis )

Your desired text will be in capture group #1

https://regex101.com/r/A86eEv/1/

var regex = /\{\d+\}$[\r\n]+([\s\S]+?)^\)$/gm;

var text = `* 1 FETCH (UID 2 BODY[] {7765}
data to be extracted
from here!
)
* 2 FETCH (UID 3 BODY[] {443}
data to be extracted
from here!
)
* 3 FETCH (UID 4 BODY[] {4432}
data to be extracted
from here!
)
* 4 FETCH (UID 5 BODY[] {123}
data to be extracted
from here!
)
SS01 OK Success`;

var matches = [...text.matchAll(regex)];
console.log(Array.from(matches,x => x[1].trim()));

You can use

/\* \d+ FETCH \(UID \d+ BODY\[] {\d+}\s*([\s\S]*?)(?=\)[\r\n]+(?:\* \d+ FETCH \(UID \d+ BODY\[] {\d+}|SS01 OK Success))/g

See the regex demo. Or, if you do not need to check all the context so thoroughly, use

/{\d+}\s*([\s\S]*?)(?=\))/g

Details:

  • \* \d+ FETCH \(UID \d+ BODY\[] {\d+} - *, space, one or more digits, space, FETCH, space, (UID, space, 1+ digits, space, BODY[], space, {, one or more digits, }
  • \s* - zero or more whitespaces
  • ([\s\S]*?) - Group 1 (the value you need to get): any zero or more chars as few as possible
  • (?=\)[\r\n]+(?:\* \d+ FETCH \(UID \d+ BODY\[] {\d+}|SS01 OK Success)) - a positive lookahead that requires the following pattern sequence immediately to the right of the current location:
    • \) - a ) char
    • [\r\n]+ - one or more CR or LF chars
    • (?:\* \d+ FETCH \(UID \d+ BODY\[] {\d+}|SS01 OK Success) - either of
      • \* \d+ FETCH \(UID \d+ BODY\[] {\d+} - *, space, one or more digits, space, FETCH, space, (UID, space, 1+ digits, space, BODY[], space, {, one or more digits, }
      • | - or
      • SS01 OK Success - a SS01 OK Success string.

JavaScript demo:

const rx = /\* \d+ FETCH \(UID \d+ BODY\[] {\d+}\s*([\s\S]*?)(?=\)[\r\n]+(?:\* \d+ FETCH \(UID \d+ BODY\[] {\d+}|SS01 OK Success))/g;
const text = '* 1 FETCH (UID 2 BODY[] {7765}\ndata to be extracted\nfrom here!\n)\n* 2 FETCH (UID 3 BODY[] {443}\ndata to be extracted\nfrom here!\n)\n* 3 FETCH (UID 4 BODY[] {4432}\ndata to be extracted\nfrom here!\n)\n* 4 FETCH (UID 5 BODY[] {123}\ndata to be extracted\nfrom here!\n)\nSS01 OK Success';
const matches = [...text.matchAll(rx)];
console.log(Array.from(matches,x => x[1].trim()));

// Or, with the simplified regex:
console.log(
   Array.from(text.matchAll(/{\d+}\s*([\s\S]*?)(?=\))/g), x => x[1].trim())
)

Related