маскировка номера кредитной карты в Java - PullRequest
8 голосов
/ 20 сентября 2011

Я попытался замаскировать символы в строке номера кредитной карты, используя символ '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, неправильный путь.

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

Ответы [ 11 ]

18 голосов
/ 15 июля 2013

Вот, пожалуйста.Чистый и многоразовый:

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

Удачи.

8 голосов
/ 30 мая 2012

Вот немного более чистая реализация, основанная на 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);
}
7 голосов
/ 14 ноября 2013

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

String ccNumber = "123232323767"; 

StringUtils.overlay(ccNumber, StringUtils.repeat("X", ccNumber.length()-4), 0, ccNumber.length()-4);
5 голосов
/ 20 сентября 2011

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

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

3 голосов
/ 29 апреля 2016
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]);
        }
    }
}
2 голосов
/ 14 июля 2016

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

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
2 голосов
/ 20 сентября 2011

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

2 голосов
/ 20 сентября 2011

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

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

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

1 голос
/ 24 октября 2018

Ниже код замаскирует 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 голос
/ 23 ноября 2012

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

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