маскировка номера кредитной карты в java

Я попытался замаскировать символы в строке номера кредитной карты, используя символ «X». Я написал две функции, как показано ниже. Вторая функция использует класс 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 занимает больше времени, чем цикл for + StringBuffer в первой функции. Очевидно, я использую API неправильно.

Может кто-нибудь посоветовать, как правильно пользоваться этим апи, в таком случае?


person jimgardener    schedule 20.09.2011    source источник
comment
Выполнение такого теста чрезвычайно сложно (несколько прогонов, уважение к прогреву, ...). Был ли загружен класс StringUtils ранее или это первый раз?   -  person Howard    schedule 20.09.2011
comment
Небольшой комментарий: используйте StringBuilder, а не StringBuffer. StringBuffer выполняет ненужную синхронизацию.   -  person Mike Brennan    schedule 22.02.2014
comment
Можете ли вы попробовать запутать номер кредитной карты, это лучший способ защитить номер кредитной карты.   -  person Manisha Srivastava    schedule 16.09.2015
comment
Первый способ сработал у меня отлично.   -  person Eben Watts    schedule 19.11.2018


Ответы (11)


Во-первых, если вы проводите измерения такого короткого кода, вы часто не получаете точных результатов из-за минимального временного разрешения, которое предоставляет ваш ЦП/библиотека/что-то еще (что означает, что вы обычно видите 0 мс или такое же маленькое значение в течение и над).

Во-вторых, и что более важно, не оптимизируйте это! «Преждевременная оптимизация — корень всех зол», и в случае, когда у вас есть всего несколько мс, которые вы хотите оптимизировать, ваши усилия будут потрачены впустую. Вам придется замаскировать миллионы кредитных карт, прежде чем вы хотя бы отдаленно задумаетесь об оптимизации этого простого метода маскирования.

person Frank    schedule 20.09.2011

Ну вот. Чистый и многоразовый:

/**
 * 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

Удачи.

person Ayman    schedule 15.07.2013
comment
какова цель переменной index здесь? - person Andrey; 05.05.2021
comment
Он отслеживает следующую цифру номера карты, которая будет добавлена ​​к замаскированной строке. Этот метод предполагает, что длина (cardNumber) = количество «x» в маске + количество «#» является маской. - person Ayman; 06.05.2021

Использование Apache StringUtils...

String ccNumber = "123232323767"; 

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

Вот немного более чистая реализация, основанная на 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);
}
person Michael-7    schedule 30.05.2012
comment
Кстати, для точного сравнения (конечно, в ходе многочисленных итераций) вам может потребоваться исключить строку: final String s = creditCardNumber.replaceAll("\\D", ""); и соответствующим образом обновить код. - person Michael-7; 30.05.2012

Я знаю, что это не ответ, но вы можете использовать регулярное выражение и решить это за один шаг.

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

Объяснение:

  • Граница \b помогает проверить, что мы являемся началом цифр (есть и другие способы сделать это, но здесь подойдет и этот).
  • (\d{4}) записывает четыре цифры в группу 1 и группу 3.
  • (\d{8}) записывает восемь цифр в группу 2
  • В замене $1 и $3 содержится контент, соответствующий группам 1 и 3.
person padippist    schedule 14.07.2016

Строковые утилиты, вероятно, копируют строку несколько раз. например, когда вы запускаете padded.concat(end); jvm выделяет новую строку размера двух строк concat и копирует их. Если вы используете StringBuffer, вы сохраняете все эти копии, так как в буфере уже выделено место, а объединенная строка только что скопирована туда. Мне кажется, что StringBuffer быстрее, хотя измеренное время кажется довольно большим, чем я ожидал.

person roni bar yanai    schedule 20.09.2011

Код ниже будет маскировать 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;
}
person Tint Naing Win    schedule 24.10.2018

Скорее всего, это время загрузки StringUtils из файла apache-commons.jar. Не реальное время выполнения.

Чтобы рассчитать реальное время выполнения, попробуйте запустить несколько раз и посмотрите, сколько мс будет 2-го. С 3-го по 100-й возьмут.

В любом случае, как сказал Фрэнк, оптимизация до такого уровня не рекомендуется.

person medopal    schedule 20.09.2011

Хотя менее читаемо, вы можете сделать это

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

Использование Google Caliper на моей машине даст около 20-25 нс по сравнению с более чем 100 нс с подходами StringBuilder или StringUtils.overlay + Repeat.

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();
        }
    }
}
person Archimedes Trajano    schedule 22.11.2012

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

Выход: ХХХХХХХХХХ6785

person Abhilash Khajuria    schedule 03.06.2016
comment
Объяснение того, как этот код отвечает на вопрос, будет полезно для будущих пользователей. - person JAL; 03.06.2016

person    schedule
comment
убедитесь, что вы включили закрывающие скобки в свой блок кода - person David Wyly; 29.04.2016