Маскировка номера кредитной карты в 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, неправильный путь..
Может кто-нибудь посоветовать, как правильно использовать этот API в этом случае?
12 ответов
Во-первых, если вы выполняете измерения такого короткого кода, вы часто не получаете точных результатов из-за минимального временного разрешения, которое ваш ЦП / библиотека / что-либо обеспечивает (что означает, что вы обычно получаете 0 мс или одно и то же небольшое значение снова и снова). над).
Во-вторых, что еще более важно, не оптимизируйте это! "Преждевременная оптимизация - это корень всего зла", и в случае, когда у вас есть всего несколько мс, которые вы хотите оптимизировать, усилия полностью пропадают. Вам придется замаскировать миллионы кредитных карт, прежде чем вы хотя бы отдаленно подумаете об оптимизации этого простого метода маски.
Ну вот. Чистый и многоразовый:
/**
* 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
Удачи.
Используя Apache StringUtils...
String ccNumber = "123232323767";
StringUtils.overlay(ccNumber, StringUtils.repeat("X", ccNumber.length()-4), 0, ccNumber.length()-4);
Вот немного более чистая реализация, основанная на 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);
}
Я знаю, что это не ответ, но вы можете использовать регулярное выражение и решить это за один шаг
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.
String Utils, вероятно, копирует строку несколько раз. например, когда вы запускаете padded.concat(end); jvm выделяет новую строку размером двух строк concat и копирует их. Если вы используете StringBuffer, вы сохраняете все эти копии, так как в буфере уже выделено место и только что скопированная строка скопирована туда. Мне кажется, что StringBuffer быстрее, хотя измеренное время кажется довольно большим, чем я ожидал.
Ниже код замаскирует 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;
}
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]);
}
}
}
Скорее всего, это время StringUtils
загружается из apache-commons.jar
файл. Не настоящее время исполнения.
Чтобы вычислить реальное время выполнения, попробуйте запустить несколько раз и посмотрите, сколько мс будет 2-ым. 3-й до 100-й займет.
В любом случае, как сказал Фрэнк, оптимизация до этого уровня не рекомендуется.
Хотя это менее читабельно, вы можете сделать это
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();
}
}
}
public class MaskCreditCard {
static void creditcard(String cc) {
StringBuffer s = new StringBuffer();
if (cc.length() <= 4) {
System.out.println(cc);
} else if (cc.length() > 16) {
System.out.println("Give a valid 16 digit CC number");
}
else {
for (int i = cc.length() - 4; i > 0; i--) {
s.append("#");
}
System.out.println(s + "" + cc.substring(cc.length() - 4));
}
}
public static void main(String[] args) {
creditcard("11111111111111111111111111111111");
}
}
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);
}
Выход положен: XXXXXXXXXXXX6785