web-dev-qa-db-ja.com

java)でクレジットカード番号をマスキングする

クレジットカード番号文字列の文字を文字「X」でマスクしようとしました。次のように2つの関数を記述しました。2番目の関数はcommons.lang.StringUtilsクラスを使用します。両方の場合にかかる時間を見つけようとしました。

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);
}

これを実行すると、この結果が得られました

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

Commons.StringUtilsが最初の関数のforloop + StringBufferよりも時間がかかる理由がわかりません。明らかに、間違った方法でapiを使用しています。

この場合、誰かがこのAPIを正しく使用する方法をアドバイスできますか?

9
jimgardener

まず、このような短期間のコードを測定すると、CPU /ライブラリ/その他が提供するタイミング解像度が最小であるため、正確な結果が得られないことがよくあります(つまり、通常、0msまたは同じ小さな値が表示されます。以上)。

次に、さらに重要なことに、これを最適化しないでください。 "時期尚早の最適化はすべての悪の根源です"そしてあなたが最適化したい数ミリ秒しかない場合、努力は完全に無駄になります。この単純なマスク方法の最適化についてリモートで考える前に、何百万ものクレジットカードをマスクする必要があります。

6
Frank

どうぞ。清潔で再利用可能:

/**
 * 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();
}

サンプル呼び出し:

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

幸運を。

21
Ayman

ApacheStringUtilsの使用...

String ccNumber = "123232323767"; 

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

これはStringUtilsに基づく少しクリーンな実装ですが、実装と比較してどのように機能するかはわかりません。とにかく、「時期尚早の最適化」コメントは非常に有効なままです。

    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);
}
8
Michael-7

これは答えではないことはわかっていますが、正規表現を使用してこれを1つのステップで解決できます

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

説明:

  • \ b境界は、数字の先頭であることを確認するのに役立ちます(これを行う方法は他にもありますが、ここではこれで十分です)。
  • (\ d {4})グループ1とグループ3の4桁をキャプチャします
  • (\ d {8})グループ2に8桁をキャプチャします
  • 置換では、$ 1および$には、グループ1および3に一致するコンテンツが含まれます。
4
padippist
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]);
        }
    }
}
3
abhinav kumar

おそらく、これはStringUtilsApache-commons.jarファイルからロードされる時間です。実際の実行時間ではありません。

実際の実行時間を計算するには、複数回実行して、2番目のミリ秒数を確認します。 3日から100日までかかります。

とにかく、フランクが言ったように、このレベルに最適化することはお勧めできません。

2
medopal

String utilsは、おそらく文字列を数回コピーします。たとえば、padded.concat(end);を実行する場合です。 jvmは、2つの連結文字列のサイズの新しい文字列を割り当ててコピーします。 StringBufferを使用する場合、バッファにはすでに場所が割り当てられており、連結された文字列がそこにコピーされているため、これらのコピーをすべて保存します。 StringBufferの方が速いと私には理解できますが、測定される時間は私が予想するよりもかなり長いようです。

2
roni

読みにくくなりますが、これを行うことができます

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

私のマシンでGoogleCaliperを使用すると、StringBuilderまたはStringUtils.overlay +繰り返しアプローチを使用した場合の100ns以上と比較して、約20〜25nsが生成されます。

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();
        }
    }
}
1

以下のコードは、文字列の75%をマスクします。

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;
}
1
Tint Naing Win