3

So I want to match credit card numbers and mask them in 6*4 format. So that only first 6 and last 4 characters will be visible. The characters between will be '*'. I tried to figure it out with a MASK like;

private static final String MASK = "$1***$3";
matcher.replaceAll(MASK);

But could not find out the way to give me back equal length of stars in the middle as the group $2.

Then I implemented the below code and it works. But what i want to ask if there is a shorter or easier way to do this. Anyone knows it?

private static final String HIDING_MASK = "**********";
private static final String REGEX = "\\b([0-9]{6})([0-9]{3,9})([0-9]{4})\\b";
private static final int groupToReplace = 2;

private String formatMessage(String message) throws NotMatchedException {
    Matcher m = Pattern.compile(REGEX).matcher(message);

    if (!m.find()) throw new NotMatchedException();
    else {
        StringBuilder maskedMessage = new StringBuilder(message);
        do {
            maskedMessage.replace(m.start(groupToReplace), m.end(groupToReplace), 
                    HIDING_MASK.substring(0, (m.end(groupToReplace) - m.start(groupToReplace))));

        } while(m.find(m.end()));

        return maskedMessage.toString();
    }
}

EDIT: Here is an example message to process. "2017.08.26 20:51 [Thread-Name] [Class-Name] [MethodName] Credit card holder 12345678901234567 02/2022 123 ........."

jaco0646
  • 15,303
  • 7
  • 59
  • 83
kerberos84
  • 300
  • 1
  • 10
  • 3
    Why use a regex for this? `String masked = num.substring(0,6) + "******" + num.substring(12,16)` – slim Aug 25 '17 at 16:01
  • If you really need a regex solution, then have a look at `Pattern#appendReplacement` – Wiktor Stribiżew Aug 25 '17 at 16:04
  • @slim Because I don't know what is and how many credit card numbers(if there is) in the string – kerberos84 Aug 25 '17 at 16:04
  • Ah, hold on, are you talking about using this to find and replace numbers in a long string containing arbitrary text with credit card numbers within? If so you should edit the question to explain that -- give an example input text. – slim Aug 25 '17 at 16:25

6 Answers6

1
private String formatMessage(String message) throws NotMatchedException { 
    if (message.matches(".*\\b\\d{13,19}\\b.*")) {
        return message.replaceAll("(?:[.\\b]*)(?<=\\d{6})\\d(?=\\d{4})(?:[.\\b]*)", "*");
    } else {
        throw new NotMatchedException() ;
    }
} 
anomeric
  • 688
  • 5
  • 17
  • I don't think this would be easy if the `message` contains the credit-card number only as a substring. – alirabiee Aug 25 '17 at 16:07
  • I am afraid you didn't understand my question. Credit card numbers are NOT always 16 digits. In my case I am looking for 13 to 19 digit cards. – kerberos84 Aug 25 '17 at 16:09
  • Thx but I already implemented a similar approach as you can see in my question. I am interested in doing this in a simpler way similar to ALI's answer. – kerberos84 Aug 25 '17 at 16:17
  • The replace all was also added to my answer – anomeric Aug 25 '17 at 16:22
  • hi your answer is still not a solution for this. I don't know what is in the 'message'. It is not consisting of a pure credit card number. An example message: "2017.08.26 20:51 [Thread-Name] [Class-Name] [MethodName] Credit card holder 12345678901234567 02/2022 123 ........." and so on. Hence I can't just check the length of the message as your answer suggest. But thx anyway. – kerberos84 Aug 26 '17 at 18:53
  • You can't expect an answer to part of a question you didn't include in your OP. I'll edit my answer – anomeric Aug 26 '17 at 18:55
  • hi, i thought it is self explanotary from the code i wrote. but anywat my fault so i added the example to the question. thanks. – kerberos84 Aug 26 '17 at 19:12
  • There were some typos in my last. These have been addressed – anomeric Aug 26 '17 at 19:19
  • hi @anomeric i get this exception; `Exception in thread "main" java.util.regex.PatternSyntaxException: Illegal/unsupported escape sequence near index 6 (?:[.\b]*)(?<=\d{6})\d(?=\d{4})(?:[.\b]*) ^ ` I changed the both `\\b` in the `replaceAll` mask to `\b` and now it does what i asked. Thanks for the answer. – kerberos84 Aug 27 '17 at 11:41
1

You can do it simply with this code:

str.replaceAll( "(?<=\\d{6})\\d(?=\\d{4})", "*" );
alirabiee
  • 1,286
  • 7
  • 14
0

Readable but uncool.

String in = "1234561231234";
String mask = in
    .replaceFirst("^\\d{6}(\\d+)\\d{4}$", "$1")
    .replaceAll("\\d", "\\*");
String out = in
    .replaceFirst("^(\\d{6})\\d+(\\d{4})$", "$1" + mask + "$2");
linden2015
  • 887
  • 7
  • 9
0

You can use the following if your text contains multiple credit-card numbers with variable lengths:

str.replaceAll( "\\b(\\d{13,19})\\b", "\u0000$1\u0000" )
   .replaceAll( "(?<=\\d{6})(?<=\u0000\\d{6,14})\\d(?=\\d{4,12}\u0000)(?=\\d{4})", "*" )
   .replaceAll( "\u0000([\\d*]+)\u0000", "$1" );

Not really readable, though, but it's all in one go.

alirabiee
  • 1,286
  • 7
  • 14
-1

A simple solution for a 16 char "number":

 String masked = num.substring(0,6) + "******" + num.substring(12,16);

For a string of arbitrary length ( >10 ):

 String masked = num.substring(0,6) 
               + stars(num.length() - 10) 
               + num.substring(num.length() - 6);

... where stars(int n) returns a String of n stars. See Simple way to repeat a String in java -- or if you don't mind a limit of 9 stars, "*********".substring(0,n)

slim
  • 40,215
  • 13
  • 94
  • 127
-1

Use a StringBuffer and overwrite the desired characters:

StringBuffer buf = new StringBuffer(num);
for(int i=4; i< buf.length() - 6) {
    buf.setCharAt(i, '*');
}
return buf.toString();

You could also use buf.replace(int start, int end, String str)

slim
  • 40,215
  • 13
  • 94
  • 127