# 'Format Value' behavior with Unmatched $regexp

**URL:** <https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300>\
**Category:** Support\
**Created:** [November 7, 2014, 10:15pm UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300 "2014-11-07T22:15:29Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![musicdemon](https://community.mp3tag.de/letter_avatar_proxy/v4/letter/m/eb8c5e/32.png) [@musicdemon](https://community.mp3tag.de/u/musicdemon)\
**Post date:** [November 7, 2014, 10:15pm UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/1 "2014-11-07T22:15:29Z")

</div>

I'm going through my classical music collection and adding values for my custom OPUS field. I'd like to be able to use a custom Action to add the Opus number to the Opus field, but **only if the opus number exists** in the title. Here's the custom action I'm currently using:

Field: OPUS  
Format Value: $regexp(%title%,'(._Opu?s?.?\s?)(\d+)._',$2)

This works fine, when there is an opus number in the title. However, when there's a regular title like so, this action just assigns the title to the Opus field, instead of leaving it null. Example:

Before Action:  
Title = Tragic Overture  
Opus = _null_

After Action:  
Title = Tragic Overture  
Opus = Tragic Overture

I'd like to be able to use this on all my tracks, without having to visually check if it exists for each track before running my custom action. Is there a way to only perform the replace if the RegEx matches?

---

<div class="post-metadata">

**Author:** ![vkostas](https://community.mp3tag.de/user_avatar/community.mp3tag.de/vkostas/32/227_2.png) [@vkostas](https://community.mp3tag.de/u/vkostas)\
**Post date:** [November 8, 2014, 2:37am UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/2 "2014-11-08T02:37:37Z")

</div>

The reaction is correct even you (and me in the past) expected otherwise.  
Let's amend a littlebit the action for your needs.

1. Match only Op/opus whole words. your regexp matches unwanted cases like **octopus** , **ops** , **opu**. Add also start/end anchors to be safe.

```
$regexp(%title%,'^.*?\bOp(?:us)?\b\.?\s*?(\d+).*?$',$1)</li><li>Handle cases without Opus.
$regexp(%title%,'(?>^.*?\bOp(?:us)?\b\.?\s*?(\d+).*?$|^.*($))',$1)</li></ol>
```

---

<div class="post-metadata">

**Author:** ![musicdemon](https://community.mp3tag.de/letter_avatar_proxy/v4/letter/m/eb8c5e/32.png) [@musicdemon](https://community.mp3tag.de/u/musicdemon)\
**Post date:** [November 8, 2014, 4:22am UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/3 "2014-11-08T04:22:50Z")

</div>

> [@Victor Kostas](#):
>
> The reaction is correct even you (and me in the past) expected otherwise.  
> Let's amend a littlebit the action for your needs.
> 
> 1. Match only Op/opus whole words. your regexp matches unwanted cases like **octopus** , **ops** , **opu**. Add also start/end anchors to be safe.
> 
> ```
> $regexp(%title%,'^.*?\bOp(?:us)?\b\.?\s*?(\d+).*?$',$1)</li><li>Handle cases without Opus.
> $regexp(%title%,'(?>^.*?\bOp(?:us)?\b\.?\s*?(\d+).*?$|^.*($))',$1)</li></ol>
> 
> ```

That worked beautifully! I've done some research on that syntax, and it looks like you're essentially forcing a match with the optional statement: **|^.\*($)**. If I understand the regex engine, $1 is set to the Opus number if the opus expression matches, but is null (capture group with line anchor) if the optional expression matches. With this method, %title% always gets replaced, whether it has an Opus number in it or not, which gives us the requested functionality. Did I get that right?

---

<div class="post-metadata">

**Author:** ![vkostas](https://community.mp3tag.de/user_avatar/community.mp3tag.de/vkostas/32/227_2.png) [@vkostas](https://community.mp3tag.de/u/vkostas)\
**Post date:** [November 8, 2014, 4:57am UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/4 "2014-11-08T04:57:30Z")

</div>

> [@musicdemon](#):
>
> That worked beautifully! I've done some research on that syntax, and it looks like you're essentially forcing a match with the optional statement: **|^.\*($)**. If I understand the regex engine, $1 is set to the Opus number if the opus expression matches, but is null (capture group with line anchor) if the optional expression matches. With this method, %title% always gets replaced, whether it has an Opus number in it or not, which gives us the requested functionality. Did I get that right?

Yes, perfectly :-).  
We needed to always have a '$1' placeholder, that's why I introduced the **|^.\*($)**. So match a null if no Opus variants found..

---

<div class="post-metadata">

**Author:** ![musicdemon](https://community.mp3tag.de/letter_avatar_proxy/v4/letter/m/eb8c5e/32.png) [@musicdemon](https://community.mp3tag.de/u/musicdemon)\
**Post date:** [November 8, 2014, 2:04pm UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/5 "2014-11-08T14:04:20Z")

</div>

> [@Victor Kostas](#):
>
> Yes, perfectly :-).  
> We needed to always have a '$1' placeholder, that's why I introduced the **|^.\*($)**. So match a null if no Opus variants found..

Thanks for the clarification. I'll post a reply to that stickied "Regular Expressions" thread with my custom actions once I've gotten a workflow figured out for composers with multiple numbering systems, like [Beethoven](http://imslp.org/wiki/List_of_works_by_Ludwig_van_Beethoven).

---

<div class="post-metadata">

**Author:** ![musicdemon](https://community.mp3tag.de/letter_avatar_proxy/v4/letter/m/eb8c5e/32.png) [@musicdemon](https://community.mp3tag.de/u/musicdemon)\
**Post date:** [November 29, 2014, 4:28pm UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/6 "2014-11-29T16:28:51Z")

</div>

I've now got a great set of custom actions defined for Opus numbers that's almost ready. The one sticking point is that I can't figure out what the regex should be for the part before the word "opus". Here's what I'm looking at for examples:

MATCH:  
a ) Opus 26, ludwig van beethoven - the song  
b ) Sonate n. 14 op. 27, n. 2 in cis-moll  
c ) IMSLP243517-beethoven\_Op5no2\_theblahsymphony

DON'T MATCH:  
d ) Purple Octopus 5 - Shouldn't Match Song

The following regex matches strings b & c, but doesn't match string a. If I take out the custom character class at the beginning, though, it matches string d. Any ideas?  
^._[\W-.\s]Op(?:us)?[-.\s]_?(\d+).\*?$

---

<div class="post-metadata">

**Author:** ![DetlevD](https://community.mp3tag.de/user_avatar/community.mp3tag.de/detlevd/32/123_2.png) [@DetlevD](https://community.mp3tag.de/u/DetlevD)\
**Post date:** [November 29, 2014, 7:56pm UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/7 "2014-11-29T19:56:56Z")

</div>

> [@musicdemon](#):
>
> ...  
> MATCH:  
> a ) Opus 26, ludwig van beethoven - the song  
> b ) Sonate n. 14 op. 27, n. 2 in cis-moll  
> c ) IMSLP243517-beethoven\_Op5no2\_theblahsymphony  
> DON'T MATCH:  
> d ) Purple Octopus 5 - Shouldn't Match Song  
> ...

This works for me ...  
TITLE MATCHES "^._[\W-.\s]Op(?:us)?[-.\s]_?(\d+).\*?$"

DD.20141129.2156.CET

---

<div class="post-metadata">

**Author:** ![musicdemon](https://community.mp3tag.de/letter_avatar_proxy/v4/letter/m/eb8c5e/32.png) [@musicdemon](https://community.mp3tag.de/u/musicdemon)\
**Post date:** [November 29, 2014, 10:06pm UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/8 "2014-11-29T22:06:31Z")

</div>

Yeah, it matches at the [regexpal.com](http://regexpal.com) tester, but not other engines or in mp3tag. I came up with a simpler way of matching the stuff before 'opus' by eliminating the custom character class and just using word boundaries. It requires an extra step to replace underscores with another character, but it's a bit cleaner. Thanks, anyway!

---

<div class="post-metadata">

**Author:** ![DetlevD](https://community.mp3tag.de/user_avatar/community.mp3tag.de/detlevd/32/123_2.png) [@DetlevD](https://community.mp3tag.de/u/DetlevD)\
**Post date:** [November 30, 2014, 8:12am UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/9 "2014-11-30T08:12:17Z")

</div>

> [@musicdemon](#):
>
> Yeah, it matches at the [regexpal.com](http://regexpal.com) tester, but not other engines or in mp3tag. ...

When I wrote ...

_This works for me ...  
TITLE MATCHES "^.[\W-.\s]Op(?:us)?[-.\s]?(\d+).\*?$"_

... then it works in Mp3tag.  
I checked it thrice using your examples.

See there for other inspiration ...  
[/t/16152/1](https://community.mp3tag.de/t/16152/1)

DD.20141130.1026.CET

---

<div class="post-metadata">

**Author:** ![musicdemon](https://community.mp3tag.de/letter_avatar_proxy/v4/letter/m/eb8c5e/32.png) [@musicdemon](https://community.mp3tag.de/u/musicdemon)\
**Post date:** [November 30, 2014, 12:44pm UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/10 "2014-11-30T12:44:31Z")

</div>

> [@DetlevD](#):
>
> When I wrote ...
> 
> _This works for me ...  
> TITLE MATCHES "^.[\W-.\s]Op(?:us)?[-.\s]?(\d+).\*?$"_
> 
> ... then it works in Mp3tag.  
> I checked it thrice using your examples.
> 
> See there for other inspiration ...  
> [/t/16152/1](https://community.mp3tag.de/t/16152/1)
> 
> DD.20141130.1026.CET

My mistake! I re-tested it using that regex and it came out fine. Thanks for linking to that other thread about opus number handling. That will come in handy 🙂

---

<div class="post-metadata">

**Author:** ![system](https://community.mp3tag.de/uploads/default/original/2X/c/ce7035d426cb755a7916793326d23b465222a407.png) [@system](https://community.mp3tag.de/u/system)\
**Post date:** [February 9, 2026, 12:25pm UTC](https://community.mp3tag.de/t/format-value-behavior-with-unmatched-regexp/16300/11 "2026-02-09T12:25:21Z")

</div>

Closed on February 9, 2026, 12:25pm UTC


