masking a creditcard number in java

痴心易碎 提交于 2020-08-21 12:51:49

问题


I tried to mask the characters in a creditcard number string using character 'X'.I wrote two functions as below .The second function uses commons.lang.StringUtils class .I tried to find the time it takes in both cases

public static String maskCCNumber(String ccnum){
        long starttime = System.currentTimeMillis();
        int total = ccnum.length();
        int startlen=4,endlen = 4;
        int masklen = total-(startlen + endlen) ;
        StringBuffer maskedbuf = new StringBuffer(ccnum.substring(0,startlen));
        for(int i=0;i<masklen;i++) {
            maskedbuf.append('X');
        }
        maskedbuf.append(ccnum.substring(startlen+masklen, total));
        String masked = maskedbuf.toString();
        long endtime = System.currentTimeMillis();
        System.out.println("maskCCNumber:="+masked+" of :"+masked.length()+" size");
        System.out.println("using StringBuffer="+ (endtime-starttime)+" millis");
        return masked;
    }

    public static String maskCCNumberCommons(String ccnum){
        long starttime = System.currentTimeMillis();
        int total = ccnum.length();
        int startlen=4,endlen = 4;
        int masklen = total-(startlen + endlen) ;
        String start = ccnum.substring(0,startlen);
        String end = ccnum.substring(startlen+masklen, total);
        String padded = StringUtils.rightPad(start, startlen+masklen,'X'); 
        String masked = padded.concat(end);
        long endtime = System.currentTimeMillis();
        System.out.println("maskCCNumber:="+masked+" of :"+masked.length()+" size");
        System.out.println("using Stringutils="+(endtime-starttime)+" millis");
        return masked;
    }

public static void ccNumberMaskingDemo() {
   String mcard1="5555555555554444";
   maskCCNumber(mcard1);
   maskCCNumberCommons(mcard1);
}

When I ran this ,I got this result

maskCCNumber:=5555XXXXXXXX4444 of :16 size
using StringBuffer=0 millis
maskCCNumber:=5555XXXXXXXX4444 of :16 size
using Stringutils=25 millis

I can't understand why commons.StringUtils is taking more time than the for loop+StringBuffer in the first function.Obviously I am using the api ,the wrong way..

Can someone advise how to use this api correctly, in this case?


回答1:


Firstly, if you make measurements of such a short-running code, you often do not get accurate results due to the minimal timing resolution your CPU/library/whatever provides (which means you usually get to see 0ms or the same small value over and over).

Second and more importantly, do not optimize this! "Premature optimization is the root of all evil" and in a case where you have only a few ms that you want to optimize the effort is thoroughly wasted. You would have to mask millions of credit cards before you should even remotely think about optimizing this simple mask method.




回答2:


Here you go. Clean and reusable:

/**
 * Applies the specified mask to the card number.
 *
 * @param cardNumber The card number in plain format
 * @param mask The number mask pattern. Use # to include a digit from the
 * card number at that position, use x to skip the digit at that position
 *
 * @return The masked card number
 */
public static String maskCardNumber(String cardNumber, String mask) {

    // format the number
    int index = 0;
    StringBuilder maskedNumber = new StringBuilder();
    for (int i = 0; i < mask.length(); i++) {
        char c = mask.charAt(i);
        if (c == '#') {
            maskedNumber.append(cardNumber.charAt(index));
            index++;
        } else if (c == 'x') {
            maskedNumber.append(c);
            index++;
        } else {
            maskedNumber.append(c);
        }
    }

    // return the masked number
    return maskedNumber.toString();
}

Sample Calls:

System.out.println(maskCardNumber("1234123412341234", "xxxx-xxxx-xxxx-####"));
> xxxx-xxxx-xxxx-1234

System.out.println(maskCardNumber("1234123412341234", "##xx-xxxx-xxxx-xx##"));
> 12xx-xxxx-xxxx-xx34

Good luck.




回答3:


Using Apache StringUtils...

String ccNumber = "123232323767"; 

StringUtils.overlay(ccNumber, StringUtils.repeat("X", ccNumber.length()-4), 0, ccNumber.length()-4);



回答4:


Here's a slightly cleaner implementation based on StringUtils, though I am not sure how it would perform in comparison to your implementations. At any rate, the 'premature optimization' comments remain very valid.

    public static String maskNumber(final String creditCardNumber) {
    final String s = creditCardNumber.replaceAll("\\D", "");

    final int start = 4;
    final int end = s.length() - 4;
    final String overlay = StringUtils.repeat(MASK_CHAR, end - start);

    return StringUtils.overlay(s, overlay, start, end);
}



回答5:


I know this is not an answer but you can use regular expression and solve this in one step

String replaced = originalCreditCardNo.replaceAll("\\b(\\d{4})(\\d{8})(\\d{4})", "$1XXXXXXXX$3");

Explanation:

  • The \b boundary helps check that we are the start of the digits (there are other ways to do this, but here this will do).
  • (\d{4}) captures four digits to Group 1 and Group 3
  • (\d{8}) captures eight digits to Group 2
  • In the replacement, $1 and $3 contain the content matched by Groups 1 and 3



回答6:


String utils probably copies the string few times. for example when you run padded.concat(end); the jvm allocates new string of the size of the two concat strings and copy them. If you use StringBuffer you saves all those copies as the buffer already has place allocated and the concated string just copied there. make sense to me that the StringBuffer is faster although, the time measured seems rather large then I would expect.




回答7:


import java.util.Scanner;
class StringTest{
    public static void main(String ar[]){
        Scanner s=new Scanner(System.in);

        System.out.println("enter account number");
        String name=s.next();
        char a[]=new char[name.length()];
        for(int i=0;i<name.length();i++){
            a[i]=name.charAt(i);
        }
        for(int i=1;i<name.length()-3;i++){
            a[i]='*';
        }

        System.out.println("your account number");
        for(int i=0;i<name.length();i++){
            System.out.print(a[i]);
        }
    }
}



回答8:


Most probably, this is the time of StringUtils being loaded from the apache-commons.jar file. Not the real execution time.

To calculate the real execution time, try to run multiple times and see how much much ms will the 2nd. 3rd up to 100th will take.

Anyway, as Frank said, optimizing to this level is not recommended.




回答9:


Below code will mask 75% of the string.

public static String mask(String input) {

    int length = input.length() - input.length()/4;
    String s = input.substring(0, length);
    String res = s.replaceAll("[A-Za-z0-9]", "X") + input.substring(length);


    return res;
}



回答10:


Although less readable you can do this

final char[] ca = in.toCharArray();
Arrays.fill(ca, left, str.length - right, 'X');
return new String(ca)

Using Google Caliper on my machine would yield about 20-25 ns compared to over 100ns with StringBuilder or StringUtils.overlay + repeat approaches.

import static org.apache.commons.lang3.StringUtils.overlay;
import static org.apache.commons.lang3.StringUtils.repeat;

import java.util.Arrays;

import org.apache.commons.lang3.StringUtils;

import com.google.caliper.Param;
import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;

public class ArrayCopyVsStringBuild extends SimpleBenchmark {

    public static void main(final String[] args) throws Exception {
        Runner.main(ArrayCopyVsStringBuild.class, args);
    }

    @Param({ "1234567890123456", "1234567890" })
    private String input;

    @Param({ "0", "4" })
    private int left;

    @Param({ "0", "4" })
    private int right;

    public void timeArray(final int reps) {
        for (int i = 0; i < reps; i++) {
            final char[] masked = input.toCharArray();
            Arrays.fill(masked, left, masked.length - right, 'X');
            final String x = new String(masked);
            x.toString();
        }
    }

    public void timeStringBuilder(final int reps) {
        for (int i = 0; i < reps; i++) {
            final StringBuilder b = new StringBuilder(input.length());
            b.append(input.substring(0, left));
            for (int z = 0; z < input.length() - left - right; ++z) {
                b.append('X');
            }
            b.append(input.substring(input.length() - right));
            final String x = b.toString();
            x.toString();
        }
    }

    public void timeStringUtils(final int reps) {
        for (int i = 0; i < reps; i++) {
            final StringBuilder b = new StringBuilder(input.length());
            b.append(input.substring(0, left));
            b.append(repeat('x', input.length() - left - right));
            b.append(input.substring(input.length() - right));
            final String x = b.toString();
            x.toString();
        }
    }

    public void timeStringUtilsOverlay(final int reps) {
        for (int i = 0; i < reps; i++) {
            final int maskLength = input.length() - left - right;
            final String x = overlay(input, repeat('x', maskLength), left,
                    maskLength + left);
            x.toString();
        }
    }
}



回答11:


String existingCCNmbr = "4114360123456785";
    int i = 0;
    StringBuffer temp = new StringBuffer();
    while(i < (existingCCNmbr .length())){
        if(i > existingCCNmbr .length() -5){
            temp.append(existingCCNmbr.charAt(i));
        } else {
            temp.append("X");
        }
        i++;
    }
       System.out.println(temp);
       }

Out put : XXXXXXXXXXXX6785



来源:https://stackoverflow.com/questions/7480608/masking-a-creditcard-number-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!