【问题标题】:mask all digits except first 6 and last 4 digits of a string( length varies )屏蔽除字符串的前 6 位和后 4 位之外的所有数字(长度不同)
【发布时间】:2015-09-09 02:10:07
【问题描述】:

我有一个卡号作为字符串,例如:

string  ClsCommon.str_CardNumbe r = "3456123434561234";

此卡号的长度可以从 16 位到 19 位不等,具体取决于要求。

我的要求是我必须显示卡号的前六位数字和后四位数字,并用字符“X”掩盖中间的其他字符。

我尝试过使用 subString 并分别为 16、17、18、19 位实现它..

我将字符串(ClsCommon.str_CardNumber)拆分为 5 个字符串(str_cardNum1、str_cardNum2、str_cardNum3、str_cardNum4、str_cardNum5 - 每个字符串有 4 位数字......剩余的数字用于第 5 个字符串)

所有字符串都放在 ClsCommon 文件中。 基于此,我实现了以下内容,效果很好:

if (ClsCommon.str_CardNumber.Length == 16) {
    txtmskcrdnum.Text = string.Concat(ClsCommon.str_cardNum1, " ", ClsCommon.str_cardNum2.Substring(0, 2), "XX", " ", "XXXX", " ", ClsCommon.str_cardNum4);

}
if (ClsCommon.str_CardNumber.Length == 17) {
    txtmskcrdnum.Text = string.Concat(ClsCommon.str_cardNum1, " ", ClsCommon.str_cardNum2.Substring(0, 2), "XX", " ", "XXXX", " ", "X", ClsCommon.str_cardNum4.Substring(1, 3), " ", ClsCommon.str_cardNum5);
}
if (ClsCommon.str_CardNumber.Length == 18) {
    txtmskcrdnum.Text = string.Concat(ClsCommon.str_cardNum1, " ", ClsCommon.str_cardNum2.Substring(0, 2), "XX", " ", "XXXX", " ", "XX", ClsCommon.str_cardNum4.Substring(2, 2), " ", ClsCommon.str_cardNum5);
}


if (ClsCommon.str_CardNumber.Length == 19) {
    txtmskcrdnum.Text = string.Concat(ClsCommon.str_cardNum1, " ", ClsCommon.str_cardNum2.Substring(0, 2), "XX", " ", "XXXX", " ", "XXX", ClsCommon.str_cardNum4.Substring(3, 1), " ", ClsCommon.str_cardNum5);
}
txtmskcrdnum.Text = ClsCommon.str_CardNumber.PadLeft(ClsCommon.str_CardNumber.Length, 'X').Substring(ClsCommon.str_CardNumber.Length - 4);

对于多个长度,上述方法没有用。

我想要一种显示前 6 位和后 4 位数字并用 X 掩盖其他数字的单一方法。 最后的字符串应该在每 4 位之间有一个空格。

【问题讨论】:

    标签: c# .net string substring masking


    【解决方案1】:

    这适用于任何卡号长度:

    var cardNumber = "3456123434561234";
    
    var firstDigits = cardNumber.Substring(0, 6);
    var lastDigits = cardNumber.Substring(cardNumber.Length - 4, 4);
    
    var requiredMask = new String('X', cardNumber.Length - firstDigits.Length - lastDigits.Length);
    
    var maskedString = string.Concat(firstDigits, requiredMask, lastDigits);
    var maskedCardNumberWithSpaces = Regex.Replace(maskedString, ".{4}", "$0 ");
    

    【讨论】:

    • 如果初始数字带有空格(或其他分隔符),例如var cardNumber = "3456 1234 3456 1234";最终结果会是不正确的一个 3456 1XX XXXX XXX1 234;另一个问题是,通常我们希望保留给定的格式(例如3456-1234-3456-1234)并且只屏蔽数字(3456-12XX-XXXX-1234)。
    • @DmitryBychenko,同意,但这超出了问题的范围。我们倾向于完全删除所有非数字字符,通过 Luhn 和 Bin 检查来验证卡号,然后将其掩码并有时会格式化。
    • 我同意 yannick 的回答。去掉所有特殊字符,屏蔽数字,然后用破折号或空格重新格式化数字。尝试从头到尾保留格式,这让您很难过。
    【解决方案2】:

    试试这个。简单直接。

    public static class StringExtensions
    {
        public static string Masked(this string source, int start, int count)
        {
            return source.Masked('x', start, count);
        }
    
        public static string Masked(this string source, char maskValue, int start, int count)
        {
            var firstPart = source.Substring(0, start);
            var lastPart = source.Substring(start + count);
            var middlePart = new string(maskValue, count);
    
            return firstPart + middlePart + lastPart;
        }
    }
    

    【讨论】:

      【解决方案3】:

      我会做这样的事情(伪 C# - 以粗略的想法为基础)。

      前面有未经测试的代码...

      string MaskDigits(string input)
      {
          //take first 6 characters
          string firstPart = input.Substring(0, 6);
      
          //take last 4 characters
          int len = input.Length;
          string lastPart = input.Substring(len - 4, 4);
      
          //take the middle part (XXXXXXXXX)
          int middlePartLenght = input.Substring(6, len - 4).Count();
          string middlePart = new String('X', 5);
      
          return firstPart + middlePart + lastPart;
      }
      

      【讨论】:

      • 我更喜欢粗体而不是大写
      【解决方案4】:

      可能的实现(接受各种格式,例如数字可以分组等):

      private static String MaskedNumber(String source) {
        StringBuilder sb = new StringBuilder(source);
      
        const int skipLeft = 6;
        const int skipRight = 4;
      
        int left = -1;
      
        for (int i = 0, c = 0; i < sb.Length; ++i) {
          if (Char.IsDigit(sb[i])) {
            c += 1;
      
            if (c > skipLeft) {
              left = i;
      
              break;
            }
          }
        }
      
        for (int i = sb.Length - 1, c = 0; i >= left; --i)
          if (Char.IsDigit(sb[i])) {
            c += 1;
      
            if (c > skipRight)
              sb[i] = 'X';
          }
      
        return sb.ToString();
      }
      
      // Tests 
      
        // 3456-12XX-XXXX-1234
        Console.Write(MaskedNumber("3456-1234-3456-1234"));
        // 3456123XXXXX1234
        Console.Write(MaskedNumber("3456123434561234"));
      

      这个实现只是掩盖了数字并保留了格式。

      【讨论】:

        【解决方案5】:

        一种方法:

        string masked = null;
        for (int i = 0; i < str_CardNumber.Length; i++) {
            masked += (i > 5 && i < str_CardNumber.Length - 4) ? 'X' : str_CardNumber[i];
            if ((i + 1) % 4 == 0)
                masked += " ";
        }
        

        【讨论】:

        • 应该是masked += (i &gt;= 6...(保留6个符号,而不是7个)
        • 实际上应该是 5!
        【解决方案6】:

        如何使用 Regex 替换特定的匹配组:

                string cardNumber = "3456123434561234";
                var pattern = "^(.{6})(.+)(.{4})$";
                var maskedNumber = Regex.Replace(cardNumber, pattern, (match) =>
                {
                   return Regex.Replace(String.Format("{0}{1}{2}",
                   match.Groups[1].Value, // the first 6 digits
                   new String('X', match.Groups[2].Value.Length), // X times the 'X' char
                   match.Groups[3].Value) /*the last 4 digits*/,".{4}", "$0 "); //finally add a separator every 4 char
                });
        

        【讨论】:

          【解决方案7】:

          我确信有一种更简洁的方法可以做到这一点:

          int currentChar = 0;
          string maskable = "11111144441111";
          
          string masked = maskable;
          int length = masked.Length;
          
          int startMaskPoint = 6;
          int endMaskPoint = length - 4 - startMaskPoint;
          
          masked = masked.Remove(startMaskPoint, endMaskPoint);
          
          int numRemoved = length - masked.Length;
          string Mask = "";
          while (numRemoved != 0)
          {
              Mask = Mask + "#";
              numRemoved--;
          }
          
          masked = masked.Insert(startMaskPoint, Mask);
          string returnableString = masked;
          while (length > 4)
          {
              returnableString = returnableString.Insert(currentChar + 4, " ");
              currentChar = currentChar + 5;
              length = length - 4;
          }
          

          【讨论】:

          • 我认为你有这个倒退 - 前 6 个和最后 4 个字符需要显示,其他所有内容都被屏蔽。
          • 在阅读了@Yannicks 的回答后,按照他的方法进行。我不知道你可以创建这样的字符串:)
          【解决方案8】:

          Linq节省编码行,小代码sn-p。

          替换为大于 6 的 (*) char 和低于 CardPan 长度减 4 的字符

          var CardPan = "1234567890123456";
          var maskedPan = CardPan.Aggregate(string.Empty, (value, next) =>
          {
              if (value.Length >= 6 && value.Length < CardPan.Length - 4)
              {
                  next = '*';
              }
              return value + next;
          });
          

          【讨论】:

            【解决方案9】:

            许多给定的解决方案都会多次解析输入。 下面我提出了一个只解析一次输入的解决方案。 但是我没有C#经验,所以函数是用Scheme写的。

            功能分为两种:

            (1) visit-first-6 解析前六个字符并将它们连接到计算的其余部分。 当 visit-first-6 解析了前六个字符时,它会调用 visit-rest。

            (2) 访问休息利用了这样一个事实,即我们可以延迟一些计算,直到我们获得更多的知识。 在这种情况下,我们等待确定是否应该显示列表中的元素,直到我们知道还剩下多少字符。

            (define (mask xs)
              (letrec ([visit-first-6 (lambda (xs chars-parsed)
                                        (cond
                                          [(null? xs)
                                           ;; Shorter than 6 characters.
                                           '()]
                                          [(< chars-parsed 6)
                                           ;; Still parsing the first 6 characters
                                           (cons (car xs)
                                                 (visit-first-6 (cdr xs)
                                                                (1+ chars-parsed)))]
                                          [else
                                           ;; The first 6 characters have been parsed.
                                           (visit-rest xs
                                                       (lambda (ys chars-left)
                                                         ys))]))]
                       [visit-rest (lambda (xs k)
                                     (if (null? xs)
                                         ;; End of input
                                         (k '() 0)
                                         ;; Parsing rest of the input
                                         (visit-rest (cdr xs)
                                                     (lambda (rest chars-left)
                                                       (if (< chars-left 4)
                                                           ;; Show the last 4 characters
                                                           (k (cons (car xs) rest)
                                                              (1+ chars-left))
                                                           ;; Don't show the middle characters
                                                           (k (cons "X"
                                                                    rest)
                                                              (1+ chars-left)))))))])
                (visit-first-6 xs
                               0)))
            

            在 Petite Chez Scheme 解释器中运行掩码

            > (mask '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18))
            (1 2 3 4 5 6 "X" "X" "X" "X" "X" "X" "X" "X" 15 16 17 18)
            > (mask '())
            ()
            > (mask '(1 2 3 4))
            (1 2 3 4)
            > (mask '(1 2 3 4 5))
            (1 2 3 4 5)
            > (mask '(1 2 3 4 5 6 7 8 9))
            (1 2 3 4 5 6 7 8 9)
            > (mask '(1 2 3 4 5 6 7 8 9 10))
            (1 2 3 4 5 6 7 8 9 10)
            > (mask '(1 2 3 4 5 6 7 8 9 10 11))
            (1 2 3 4 5 6 "X" 8 9 10 11)
            

            注意。我认为这是一个有趣的练习,我想我不妨分享一下。 Yannick Meeus 已经提供了一个易于理解的解决方案。 因此,这仅适用于感兴趣的人。

            【讨论】:

              【解决方案10】:
              str.substring(0, 5) +
                              str.substring(5, str.length() - 3)
                              .replaceAll("[\\d]", "x") +
                              str.substring(str.length() - 3, str.length());
              

              //这里是简单的做法

              【讨论】:

                【解决方案11】:

                我认为这是最简单的形式。 PadLeft/PadRight 没有帮助我。我不知道也没有在建议的列表中找到,但 Right 函数不在 asp.net core c# 代码中。

                假设我有信用卡号(“2512920040512345”)并屏蔽除最后 4 位数字(“XXXXXXXXXXXX2345”)以外的所有数字。

                //C# code will give you the desired output. Replace the string with your CreditCardno variable
                new string('X', "2512920040512345".Length-4)+ "2512920040512345".Substring( "2512920040512345".Length-4, 4)
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-01-22
                  • 2020-02-10
                  • 2019-07-15
                  • 1970-01-01
                  • 2015-02-17
                  • 1970-01-01
                  相关资源
                  最近更新 更多