27

I am trying to mask email address with "*" but I am bad at regex.

input : nileshxyzae@gmail.com
output : nil********@gmail.com

My code is

String maskedEmail = email.replaceAll("(?<=.{3}).(?=[^@]*?.@)", "*");

but its giving me output nil*******e@gmail.com I am not getting whats getting wrong here. Why last character is not converted? Also can someone explain meaning all these regex

nilesh
  • 1,483
  • 2
  • 19
  • 37

7 Answers7

41

Your look-ahead (?=[^@]*?.@) requires at least 1 character to be there in front of @ (see the dot before @).

If you remove it, you will get all the expected symbols replaced:

(?<=.{3}).(?=[^@]*?@)

Here is the regex demo (replace with *).

However, the regex is not a proper regex for the task. You need a regex that will match each character after the first 3 characters up to the first @:

(^[^@]{3}|(?!^)\G)[^@]

See another regex demo, replace with $1*. Here, [^@] matches any character that is not @, so we do not match addresses like abc@example.com. Only those emails will be masked that have 4+ characters in the username part.

See IDEONE demo:

String s = "nileshkemse@gmail.com";
System.out.println(s.replaceAll("(^[^@]{3}|(?!^)\\G)[^@]", "$1*"));
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • Second regex can be simplified to `(^[^@]{3}|(?!^)\G)[^@]` – nhahtdh Oct 13 '15 at 10:42
  • @nhahtdh: Or `(^[^@]{3}|(?!^)\G)[^@](?=[^@]*@)`. What is interesting here is that after removing the look-ahead the number of steps to complete the match increases. – Wiktor Stribiżew Oct 13 '15 at 10:49
  • You need to check the checkbox "Disable internal engine optimization" in the debugger. After you do so, they have the same number of steps for the failing case, and the one without look-ahead has less steps in the success cases. In Java, there is no optimization for the case with look-ahead. – nhahtdh Oct 13 '15 at 10:57
  • Thank you for reminding about the hint on how to use regex101 for debugging Java regex better :) Perhaps, a possessive quantifier can be used to overcome that drawback, but I agree removing it is a valid way to proceed here. – Wiktor Stribiżew Oct 13 '15 at 11:01
  • I want to skip the one character before `@` and i am using `(?<=.{3}).(?=[^@]*?@)` regex. So the output of `mianabdulmateen@gmail.com` should be `mia***********n@gmail.com`. How can i achieve this? – Mateen Chaudhry Jan 28 '19 at 03:53
  • 1
    @MateenChaudhry Use `(?<=.{3}).(?=[^@]+@)`, see [demo](https://regex101.com/r/s5XaWl/1). – Wiktor Stribiżew Jan 28 '19 at 07:28
  • I use s.replaceAll("(^[^@]{3}|(?!^)\\G)[^@]", "$1*").replaceFirst("[*]+", "***") to get nil***@gmail.com because I want to mask the char count as well – MUH Mobile Inc. May 08 '19 at 14:05
  • @MUHMobileInc. I doubt you need that complex solution then. Use `s.replaceFirst("^([^@]{3})[^@]+", "$1***")` – Wiktor Stribiżew May 08 '19 at 14:07
  • Need help in here -> https://stackoverflow.com/questions/75783919/email-masking-using-regex – IRON MAN Mar 19 '23 at 18:06
19

If you're bad at regular expressions, don't use them :) I don't know if you've ever heard the quote:

Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.

(source)

You might get a working regular expression here, but will you understand it today? tomorrow? in six months' time? And will your colleagues?

An easy alternative is using a StringBuilder, and I'd argue that it's a lot more straightforward to understand what is going on here:

StringBuilder sb = new StringBuilder(email);
for (int i = 3; i < sb.length() && sb.charAt(i) != '@'; ++i) {
  sb.setCharAt(i, '*');
}
email = sb.toString();

"Starting at the third character, replace the characters with a * until you reach the end of the string or @."

(You don't even need to use StringBuilder: you could simply manipulate the elements of email.toCharArray(), then construct a new string at the end).

Of course, this doesn't work correctly for email addresses where the local part is shorter than 3 characters - it would actually then mask the domain.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
9

Your Look-ahead is kind of complicated. Try this code :

public static void main(String... args) throws Exception {
    String s = "nileshkemse@gmail.com";
    s= s.replaceAll("(?<=.{3}).(?=.*@)", "*");
    System.out.println(s);
}

O/P :

nil********@gmail.com
TheLostMind
  • 35,966
  • 12
  • 68
  • 104
4
//In Kotlin

val email = "nileshkemse@gmail.com"
val maskedEmail = email.replace(Regex("(?<=.{3}).(?=.*@)"), "*")
Ajay Prajapati
  • 668
  • 1
  • 10
  • 18
  • 6
    While your code may provide the answer to the question, please add context around it so others will have some idea what it does and why it is there. – Theo Jun 12 '19 at 11:19
  • 1
    Thank for your suggestion.In java we have replaceAll but in kotlin we do not have.It is posted for kotlin learning user. – Ajay Prajapati Jun 13 '19 at 07:13
3

I like this one because I just want to hide 4 characters, it also dynamically decrease the hidden chars to 2 if the email address is too short:

public static String maskEmailAddress(final String email) {
    final String mask = "*****";
    final int at = email.indexOf("@");
    if (at > 2) {
        final int maskLen = Math.min(Math.max(at / 2, 2), 4);
        final int start = (at - maskLen) / 2;
        return email.substring(0, start) + mask.substring(0, maskLen) + email.substring(start + maskLen);
    }
    return email;
}

Sample outputs:

my.email@gmail.com    >    my****il@gmail.com
info@mail.com         >    i**o@mail.com
user1079877
  • 9,008
  • 4
  • 43
  • 54
3
    public static string GetMaskedEmail(string emailAddress)
    {
        string _emailToMask = emailAddress;
        try
        {
            if (!string.IsNullOrEmpty(emailAddress))
            {
                var _splitEmail = emailAddress.Split(Char.Parse("@"));
                var _user = _splitEmail[0];
                var _domain = _splitEmail[1];

                if (_user.Length > 3)
                {
                    var _maskedUser = _user.Substring(0, 3) + new String(Char.Parse("*"), _user.Length - 3);
                    _emailToMask = _maskedUser + "@" + _domain;
                }
                else
                {
                    _emailToMask = new String(Char.Parse("*"), _user.Length) + "@" + _domain;
                }
            }
        }
        catch (Exception) { }
        return _emailToMask;
    }
Deep
  • 63
  • 12
0

Kotlin Extension

/**
 * Format email id in the following format
 * (m••••••n@gmail.com)
 * only exception for 2 character username in that case it will be ••@gmail.com
 * @return formatted email
 */
fun String.maskEmailId(): String {
    return if (this.isNotNullOrEmpty() && this.indexOf('@') > 0) {
        val index = this.indexOf('@')
        val maskedUsername = if (index > 2) {
            "${this.substring(0, 1)}${"*".repeat(index-2)}${this.substring(index-1)}"
        } else {
            "*".repeat(index) + this.substring(index)
        }
        maskedUsername
    }  else ""
}
MarGin
  • 2,078
  • 1
  • 17
  • 28