【问题标题】:masking a creditcard number in java在java中屏蔽信用卡号
【发布时间】:2011-11-20 18:58:29
【问题描述】:

我尝试使用字符“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 loop+StringBuffer 花费更多时间。显然我使用的是 api,错误的方式..

在这种情况下,有人可以建议如何正确使用这个 api 吗?

【问题讨论】:

  • 进行这样的基准测试非常复杂(多次运行,尊重预热,...)。 StringUtils 类是之前加载的还是第一次加载?
  • 次要评论:使用 StringBuilder 而不是 StringBuffer。 StringBuffer 会进行不必要的同步。
  • 你可以尝试混淆信用卡号码,这是保护信用卡号码的更好方法..
  • 第一种方法非常适合我。

标签: java masking apache-commons-lang


【解决方案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;
}

【讨论】:

    【解决方案2】:

    我知道这不是答案,但您可以使用正则表达式并一步解决此问题

    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 组匹配的内容

    【讨论】:

      【解决方案3】:
      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

      【讨论】:

      • 对此代码如何回答问题的解释将对未来的用户有益。
      【解决方案4】:
      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]);
              }
          }
      }
      

      【讨论】:

      • 确保在代码块中包含右括号
      【解决方案5】:

      这是一个基于 StringUtils 的更简洁的实现,但我不确定与您的实现相比它会如何执行。无论如何,“过早优化”的 cmets 仍然非常有效。

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

      【讨论】:

      • 顺便说一句,为了进行准确的比较(当然要经过多次迭代),您可能需要删除以下行:final String s = creditCardNumber.replaceAll("\\D", ""); 并相应地更新代码。
      【解决方案6】:

      使用 Apache StringUtils...

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

      【讨论】:

        【解决方案7】:

        给你。干净且可重复使用:

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

        祝你好运。

        【讨论】:

        • 这里index变量的作用是什么?
        • 它跟踪要添加到掩码字符串的下一个卡号数字。此方法假设长度(cardNumber)=掩码中'x'的个数+'#'的个数是掩码。
        【解决方案8】:

        虽然可读性较差,但您可以这样做

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

        在我的机器上使用 Google Caliper 大约会产生 20-25 ns,而使用 StringBuilder 或 StringUtils.overlay + 重复方法则超过 100ns。

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

        【讨论】:

          【解决方案9】:

          String utils 可能会复制字符串几次。例如当你运行 padded.concat(end); jvm分配两个concat字符串大小的新字符串并复制它们。如果您使用 StringBuffer ,则会保存所有这些副本,因为缓冲区已经分配了位置,并且连接的字符串刚刚复制到那里。对我来说,StringBuffer 更快是有道理的,尽管测量的时间似乎比我预期的要大。

          【讨论】:

            【解决方案10】:

            很可能,这是从apache-commons.jar 文件加载StringUtils 的时间。不是真正的执行时间。

            要计算真正的执行时间,请尝试运行多次,看看第二次会有多少毫秒。第 3 到第 100 将需要。

            无论如何,正如弗兰克所说,不建议优化到这个级别。

            【讨论】:

              【解决方案11】:

              首先,如果您测量这样一个运行时间较短的代码,由于您的 CPU/库/任何提供的最小时间分辨率(这意味着您通常会看到 0ms 或同样小一遍又一遍的价值)。

              其次,更重要的是,不要优化这个! “过早的优化是万恶之源”,在你想要优化的时间只有几毫秒的情况下,你的努力完全是白费了。在远程考虑优化这种简单的屏蔽方法之前,您必须屏蔽数百万张信用卡。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-02-22
                • 2023-03-13
                • 2011-11-11
                • 2013-12-25
                • 1970-01-01
                • 1970-01-01
                • 2018-08-13
                相关资源
                最近更新 更多