mascarar todos os dígitos, exceto os primeiros 6 e os últimos 4 dígitos de uma string (o comprimento varia)

Eu tenho um número de cartão como uma string, por exemplo:

string ClsCommon.str_CardNumbe r = "3456123434561234"; 

A extensão desse número de cartão pode variar de 16 a 19 dígitos, dependendo do requisito.

Minha exigência é que eu tenha que mostrar os primeiros seis dígitos e os últimos 4 dígitos de um número de cartão e mascarar os outros personagens entre com o caractere ‘X’.

Eu tentei usar subString e implementado separadamente para 16,17,18,19 dígitos ..

Eu dividir string (ClsCommon.str_CardNumber) para 5 strings (str_cardNum1, str_cardNum2, str_cardNum3, str_cardNum4, str_cardNum5 – 4 dígitos para cada seqüência de caracteres .. permanecendo dígitos para 5ª seqüência)

Todas as strings são colocadas no arquivo ClsCommon. Com base nisso eu implementei o abaixo, que funciona perfeitamente:

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

Para vários comprimentos, a abordagem acima não é útil.

Eu quero uma única abordagem que exibe os primeiros 6 e últimos 4 dígitos e mascara outros dígitos com X. A seqüência final deve ter um espaço entre cada 4 dígitos.

   

Isso funcionará com qualquer comprimento de número de cartão:

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

Eu faria algo assim (pseudo C # – tome como idéia aproximada para construir).

Código não testado antes …

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

Tente este. Simples e direto.

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

Implementação possível (aceita formatos varios, por exemplo, números podem ser divididos em grupos, etc.):

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

essa implementação apenas mascara os dígitos e preserva o formato.

Um método:

 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 += " "; } 

Tenho certeza de que existe uma maneira mais limpa de fazer isso:

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

Que tal replace um grupo combinado específico usando o 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 }); 

Muitas das soluções dadas analisam a input várias vezes. Abaixo, apresento uma solução que analisa a input apenas uma vez. Mas eu não tenho experiência em C #, então a function está escrita em Scheme.

A function é dividida em dois:

(1) visit-first-6 analisa os seis primeiros caracteres e concatena-os com o resto do cálculo. Quando a primeira visita-6 analisa os seis primeiros caracteres, ela chama o access de visita.

(2) visit-rest explora o fato de que podemos atrasar alguns cálculos até termos mais conhecimento. Neste caso, esperamos para determinar se o elemento na lista deve ser mostrado até sabermos quantos caracteres restam.

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

Executando a máscara no interpretador do 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) 

NB Eu vi isso como um exercício engraçado e achei que poderia compartilhá-lo. Yannick Meeus já forneceu uma solução facilmente compreensível. Então, isso serve apenas para os interessados.

Linq economiza linhas de codificação, pequeno trecho de código.

Substitui por (*) char acima de 6 e abaixo de CardPan de comprimento menos 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; });