Fix regular expression to extract key-value pairs from file names

Viewed 88

I am struggling to fix the following regular expression to extract key-value pairs from file names. File names are of the format:

FilePrefix_Key1=<0 or more spaces><optional +, - or none><optional decimal or integer value><0 or more spaces><unit/setting which may contain letters, and math operators e.g., slash>_Key2= ... .ext

I wrote the following regular expression (https://regexr.com/5lflm)

(?<=Set= *)([+-]?\d*\.?\d+?) *([A-Za-z-+^/*()]{1}[A-Za-z0-9-+^/*()]*)(?= *_|\.|\s)

but it fails on the following text as "High" is not returned in the second capture group.

FilePrefix_Speed= 3.5 kmph_Accel= +0.5 m/(s^2) _Height=+7m_Set=High.txt

Although it can be captured correctly by changing ([+-]?\d*\.?\d+?) to ([+-]?\d*\.?\d*?) but then "Accel" key is not captured properly (only +0 is returned in the second capture group).

Could you please help me to fix this expression?

Thank you!

2 Answers

Maybe this regex can help:

(_[^=]+)=\s*([^_\n]+)(?=_|\.\w{3}$)

Demo: https://regex101.com/r/9mMQn2/1/

  • (_[^=]+) capture key that starts with an underscore, match if not an equal sign
  • =\s* match equal sign and optional spaces
  • ([^_\n]+) match value, cannot be an underscore or new line
  • (?=_|\.\w{3}$) positive look-ahead for underscore of next key or file extension

You can add a \d+\.\d* alternative to the first capturing group that will allow matching one or more digits, a dot and then any zero or more digits:

Accel=\s*([+-]?(?:\d*\.?\d+|\d+\.\d*))\s*((?!\d)[A-Za-z\d+^/*()-]+)(?=\s*[_.\s])

See the regex demo. Note I replaced literal spaces with \s, and shrunk the _|\.|\s to [_.\s].

See the JavaScript demo:

const texts = ['FilePrefix_Speed= 3.5 kmph_Accel= +0.5 m/(s^2) _Height=+7ms_Set=High.txt', 'FilePrefix_Speed= 3.5 kmph_Accel= +0. m/(s^2) _Height=+7ms_Set=High.txt'];
const regex = /Accel=\s*([+-]?(?:\d*\.?\d+|\d+\.\d*))\s*((?!\d)[A-Za-z\d+^/*()-]+)(?=\s*[_.\s])/g;
for (const s of texts) {
  console.log(s, '=>', Array.from(s.matchAll(regex), m => `Group 1: ${m[1]}, Group 2: ${m[2]}`) );
}

Related