Replace last word with asterisk, or last two words

Viewed 875

I need to hide surname of persons. For persons with three words in their name, just hide last word, ej:

Laura Torres Bermudez

shoud be

Laura Torres ********

and for

Maria Fernanda Gonzales Lopez

should be

Maria Fernanda ******** *****

I think they are two regex because based on the number of words, regex will be applied.

I know \w+ replaces all word by a single asterisk, and with (?!\s). I can replace chars except spaces. I hope you can help me. Thanks.

This is my example: https://regex101.com/r/yW4aZ3/942

3 Answers

Try this:

(?<=\w+\s+\w+\s+.*)[^\s]

Explanation:

  • ?<= is a negative lookbehind - match only occurrences preceded by specified pattern
  • [^\s] / match everything except whitespace (what you used - (?!\s). - is actually weird use of lookahead - "look to next character, if it is not a whitespate; then match any character")
  • summary: replace any non-whitespace space character preceded by at least two sequences of letters (\w) and spaces (\s).

Just note that it won't hide anything for persons with only two words in their name (which is common in many countries).

Also, the regex has to be slightly modified for that testing tool to match one name per line - see https://regex101.com/r/yW4aZ3/943 (^ was added to match from start of each line and a "multi line" flag was set).

A JavaScript solution that does not rely on the ECMAScript 2018 extended regex features is

s = s.replace(/^(\S+\s+\S+)([\s\S]*)/, function($0, $1, $2) {return $1 + $2.replace(/\S/g, '*');})

Details:

  • ^ - start of string
  • (\S+\s+\S+) - Group 1: one or more non-whitespaces, 1 or more whitespaces and then 1 or more non-whitespaces
  • ([\s\S]*) - Group 2: any 1 or more chars.

The replacement is Group 1 contents and the contents of Group 2 with each non-whitespace char replaced with an asterisk.

Java solution:

s = s.replaceAll("(\\G(?!^)\\s*|^\\S+\\s+\\S+\\s+)\\S", "$1*");

See the regex demo

Details

  • (\G(?!^)\s*|^\S+\s+\S+\s+) - Group 1: either then end of the previous match (\G(?!^)) and 0 or more whitespaces or (|) 1+ non-whitespaces, 1+ whitespaces and again 1+ non-whitespaces, 1+ whitespaces at the start of the string
  • \S - a non-whitespace char.

Interested if this can be done in JavaScript without a callback, I came up with

str = str.replace(/^(\w+\W+\w+\W+\b)\w?|(?!^)(\W*)\w/gy, '$1$2*');

See this demo at regex101

The idea might look a bit confusing but it seems to work fine. It should fail on one or two words but start as soon, as there appears a word character after the first two words. Important to use the sticky flag y which is similar to the \G anchor (continue on last match) but always is bound to start.

To not add an additional asterisk, the ...\b)\w?... part after the first two words is essential. The word boundary will force a third word to start but the first capturing group is closed after \b and the first character of the third word will be consumed but not captured to correctly match the asterisk count.

The second capturing group on the right side of the alternation will capture any optional non word characters appearing between any words after the third one.

var strs = ['Foo', 'Foo Bar B', 'Laura Torres Bermudez', 'Maria Fernanda Gonzales Lopez'];
strs = strs.map(str => str.replace(/^(\w+\W+\w+\W+\b)\w?|(?!^)(\W*)\w/gy, '$1$2*'));
console.log(strs);

Related