Regular expression excluding a specific optional suffix in

Viewed 347

I'm willing to match words based on [Ssinn] in the middle of a phrase :

Sinn, Sinne, Sinnes, sinnbelebte, sinnbelebten, sinnbelebter, sinnerfüllenden, sinnerfüllendem, Sinnesgehalt, sinngebenden, Sinnesbestände, Sinnlosigkeit, Sinnesmodifikation, sinnleeren Zeichen, sinnverleihende Akte, Widersinn, widersinnigen Ausdruck

But to exclude the following ones starting with -lich suffix

Sinnlichkeit

For example in the following phrase, sinngebenden should match but not sinnliche

Also im sinngebenden Aktcharakter, der ein ganz anderer ist, je nachdem das Interesse auf das sinnliche Zeichen oder auf das mittels des Zeichens vorstellig gemachte (wenn auch durch keinerlei Phantasievorstellung verbildlichte) Objekt gerichtet ist, liegt die Bedeutung

So far I had come-up with

(?:\S+)?(?:[Ss]inn)(?:\S+)?

But in order to exclude the lich suffix, I tried

(?:\S+)?(?:[Ss]inn)((?!lich)(?:\S+))?

which does not seem to work with Javascript RegEx...

Thanks!

2 Answers

The suggested regex is

\b[Ss]inn(?!lich)[a-z]*\b

Of course, if you want to allow other characters, such as the hyphen or letters from the German alphabet such as ä, ö and ü, in the rest of the word, just add them to the character set [a-z] or alter it accordingly.

Note that if you are using a regex implementation that does not consider non-ASCII letters to be word characters, then the use of the word boundary assertions \b would be unreliable.

An alternative strategy could be to use a negative lookbehind to assert that there isn't a character in the German alphabet preceding the [Ss]inn, and to omit the trailing \b. For example:

(?<![a-zA-ZäöüßÄÖÜẞ])[Ss]inn(?!lich)[a-zA-ZäöüßÄÖÜẞ]*

That would still match the Sinn in Sinn4U, but that probably isn't an issue and could be disallowed by a lookahead if necessary.

It is not clear why you were beginning your regex with (?:\S+)?, as that is equivalent to \S*, and means match zero or more non-whitespace characters.

The problem is not easy to solve as you need full regex Unicode support. With JavaScript environments that are not ECMAScript 2018+ compatible, you will need to use some work-arounds, that might fail in some edge cases.

Here are two short regexps, the first one requires ECMAScript 2018+ support, the second one does not:

/\b[Ss]inn(?!lich)\p{L}*/gu
/\b[Ss]inn(?!lich)[a-zA-ZäöüßÄÖÜ]*/g

See the regex demo #1 and regex demo #2.

Note the [a-zA-ZäöüßÄÖÜ] character class matches any letter from a German alphabet + ASCII letters, see Regular expression not working for at least one European character.

Details:

  • \b - a word boundary
  • [Ss]inn - S or s and inn
  • (?!lich) - not lich allowed immediately to the right
  • \p{L}* - zero or more Unicode letters
  • [a-zA-ZäöüßÄÖÜ]* - zero or more ASCII or German letters.

However, if the word boundary \b does not help, and you get matches that start from a middle of the word, you can only emulate the Unicode word boundary by changing (?:(?<=^|\W)(?=\w)|(?<=\w)(?=\W|$)) into

const w = String.raw`[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]`;
const nw = String.raw`[^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]`;
const uwb = String.raw`(?:(?<=${nw}|^)(?=${w})|(?<=${w})(?=${nw}|$))`;

const regex = new RegExp(String.raw`${uwb}[Ss]inn(?!lich)\p{Alphabetic}*`, 'gu');

And this requires ECMAScript 2018+ compliant RegExp. See the following JavaScript demo:

const text = 'Also im sinngebenden Aktcharakter, der ein ganz anderer ist, je nachdem das Interesse auf das sinnliche Zeichen oder auf das mittels des Zeichens vorstellig gemachte (wenn auch durch keinerlei Phantasievorstellung verbildlichte) Objekt gerichtet ist, liegt die Bedeutung';
const w = String.raw`[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]`;
const nw = String.raw`[^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]`;
const uwb = String.raw`(?:(?<=${nw}|^)(?=${w})|(?<=${w})(?=${nw}|$))`;

const regex = new RegExp(String.raw`${uwb}[Ss]inn(?!lich)\p{Alphabetic}*`, 'gu');

console.log(text.match(regex));

Related