У меня возникла проблема, когда в CreditCardNo есть пробел до/после.
Например from INV 2420852290 to SAV 0165487
. Здесь 2420852290
— 10 цифр, но он все равно маскируется.
Для номера кредитной карты диапазон составляет 12–19 цифр. Причина в пробеле (до и после цифр), который, я думаю, занимает дополнительные 11-й и 12-й символы.
Используемое регулярное выражение
(?<=(?<![\d-*])(?=(?:-?[\d*\s]){12,19}(?![\d-*\s]))[\d-*\s]*)[\d*](?!(?:-?[\d*\s]){0,3}(?![\d-*\s]))
Я попробовал код ниже с регулярным выражением выше. Ожидается, что все сценарии, включая тот, о котором идет речь, должны работать как есть. Последние 4 цифры всегда должны быть замаскированы знаком x
.
Их можно протестировать по URL — https://dotnetfiddle.net/Gopzoz. Спасибо
public static string MaskNewCCNo(this string value)
{
var a = Regex.Replace(value, @"(?<=(?<![\d-*])(?=(?:-?[\d*\s]){12,19}(?![\d-*\s]))[\d-*\s]*)[\d*](?!(?:-?[\d*\s]){0,3}(?![\d-*\s]))", "x");
return a;
}
Да, {0,3} выглядит так, как будто он маскирует последние 3 цифры, но на самом деле, если вы видите результат в dotnetfiddle.net/Gopzoz, он на самом деле маскирует последние 4 цифры. Конечная цель — замаскировать последние 4 цифры и пройти все остальные сценарии, упомянутые в этой ссылке. Спасибо
Еще одно решение, с которым я столкнулся, заключалось в использовании этого кода:
частная статическая строка MaskCreditCardNumber (текст этой строки) { if (string.IsNullOrEmpty (текст)) возвращаемый текст; return Regex.Replace(text, "[0-9][0-9 ]{12,}[0-9]", match => { string digits = string.Concat(match.Value .Where(c => char .IsDigit(c))); return digits.Length >= 12 || digits.Length <= 19 ? новая строка('X', digits.Length - 4) + digits.Substring(digits.Length - 4) : match .Ценить; }); }
Все сценарии, упомянутые в ссылке, будут работать с этим, но для такой строки, как «1234-5678-9123-4567-891», замаскированный вывод должен отображать «xxxx-xxxx-xxxx-xxx7-891», но он отображается как « хххх-хххх-хххх-хххх-891». Если мы сможем решить эту проблему, я думаю, что все сценарии будут учтены.
пожалуйста, включите тестовые примеры в свой вопрос, чтобы любой, кто читает ваш вопрос, знал, в чем именно вам нужна помощь.
Как я уже упоминал, существует множество тестовых случаев, о которых необходимо позаботиться при устранении этой проблемы. Эти тестовые примеры являются частью dotnetfiddle.net/Gopzoz. Здесь, в этом конкретном вопросе, мой случай «от INV 2420852290 до SAV 0165487» не должен маскироваться, поскольку 2420852290 находится за пределами диапазона 12-19 цифр, но, поскольку вокруг него есть пробелы, регулярное выражение маскирует его,
Также посмотрите эту \G идею (обновлена ваша демо-версия). Другая демо (без \G
) здесь. Оба регулярных выражения неэффективны, не знаю, что хуже. :D
Круто, спасибо @bobblebubble. Я так понимаю, вы упомянули, что может быть хуже (с \G или без \G) :). Но с точки зрения производительности, вы бы посоветовали использовать \G или без него? Хотя оба решения работают для меня.
@user3665818 user3665818 Я бы выбрал шаблон на основе \G
. Вероятно, он примерно в 10-20 раз эффективнее другого. Другое регулярное выражение выполняет всю конструкцию просмотра-проверки для каждой цифры, тогда как \G
выполняет просмотр назад только один раз (от того места, где оно объединяет совпадения) и только менее затратную предпросмотровую проверку для каждого совпадения. Я думаю, что даже другое решение, которое я предоставил, примерно в 5-10 раз быстрее, чем ваше текущее регулярное выражение... вероятно, решение Виктора будет работать лучше. Чтобы это выяснить, сделайте тест.
Вы можете добавить декларацию using System.Linq;
и использовать
Regex.Replace(value, @"(?<!\d)\d(?:[ -]?\d){11,18}(?!\d)", m => Regex.Replace(m.Value, @"\d(?=(?:[\s-]?\d){4})", "x"))
Посмотрите демонстрацию регулярных выражений .
Подробности:
Получите совпадения 12–19 чисел, разделенных необязательным -
или пробелом, см. эту демонстрацию регулярных выражений.
Замаскируйте цифры внутри совпадения. Более подробная информация:
\d
— цифра, которая...(?=(?:[\s-]?\d){4})
— сразу за ним следуют четыре необязательных -
или пробела и цифра.ПРИМЕЧАНИЕ. Вы можете использовать аналогичную логику для обработки номера счета.
См. обновлённую демонстрацию кода здесь.
Это работает во всех сценариях, кроме «Код счета 3038 — Квитанция № 351461637 221414441406». Здесь «351461637» — номер счета, а «221414441406» — CCno. Теперь он отлично работает для CCno, но для номера учетной записи он все маскирует. Он должен маскировать только последние 4 цифры. Таким образом, это должно выглядеть так: «Код счета 3038 — номер квитанции xxxxx1637 xxxxxxxx1406». Это регулярное выражение, которое я использую (?<!\d)\d(?:[\s-]?\d){7,8}(?!\d)", m => Regex.Replace(m.Value , @"\d(?=(?:[\s-]?\d){4}). Номер счета должен состоять только из 8 или 9 цифр.
Я знаю, что это другой сценарий, которого не было в вопросе, но, поскольку все решения есть на этой странице, я не поднимаю новый вопрос,
Я опубликовал другой вопрос для обработки сценариев, связанных с моим последним комментарием. stackoverflow.com/questions/78781466/…. Пожалуйста, посмотрите и предложите.
Я также хотел отметить этот ответ как правильный, но он позволяет мне отметить только 1 правильный ответ.
Обработку номера вашего счета можно выполнить аналогичным образом, без использования сложных регулярных выражений, с которыми вы сами не сможете справиться. Вы не предоставили никаких требований к регулярному выражению номера счета, я бы включил его в свой ответ. ЕСЛИ номер счета представляет собой блок из 8–9 цифр, разделенных дефисами/пробелами, вы можете использовать решение, указанное в вашем комментарии, вы просто использовали TestCC
вместо TestCCAndAccNo
.
Мне нужно использовать метод TestCCAndAccNo, потому что есть свободное текстовое поле, в котором пользователь может ввести любой текст вместе с номером счета и CCNo. Тогда оба этих числа должны быть замаскированы. Я вызываю MaskCCNo и MaskAccNo из метода MaskIfContainCreditCardPanorAcctNumNew. Все сценарии, кроме «TestCCAndAccNo(»Код счета 3038 — Номер квитанции 3514611 221414441406», «Код счета 3038 — Номер квитанции 3514 611 xxxxxxxx1406»)» проходят успешно. Здесь, поскольку 3514611 содержит менее 8 цифр, MaskCCNo маскирует его, что неверно. Все должно остаться как есть. См. dotnetfiddle.net/l2PDIw
@user3665818 user3665818 Совпадения нет, см. демонстрацию регулярных выражений. Проблема в том, что предыдущее регулярное выражение (?<!\d)\d(?:[\s-]?\d){11,18}(?!\d)
соответствует 3514611
. Итак, разрешение зависит от того, каким требованиям вы хотите следовать. Может быть, мы можем предположить, что числа от 5 до 8 цифр не могут быть сопоставлены с помощью регулярного выражения CCNo? Попробуйте Regex.Replace(value, @"(?<!\d)(?!\d{5,8}(?!\d))\d(?:[\s-]?\d){11,18}(?!\d)", m => Regex.Replace(m.Value, @"\d(?=(?:[\s-]?\d){4})", "x"))
Вам необходимо учитывать конечные и ведущие пробелы в регулярном выражении. В настоящее время это не так.
Вот пример, как это сделать.
void Main()
{
string input = "From INV 2420852290 to SAV 0165487";
string pattern = @"(?<=\b(?<![\d-*])(?=\d{12,19}\b)[\d*\s]*)[\d*](?!(?:-?[\d*\s]){0,4}(?![\d-*\s]))";
string result = Regex.Replace(input, pattern, "X");
Console.WriteLine(result);
}
Это возвращает: From INV 2420852290 to SAV 0165487
Если я увеличу число на два следующим образом:
void Main()
{
string input = "From INV 242085229012 to SAV 0165487";
string pattern = @"(?<=\b(?<![\d-*])(?=\d{12,19}\b)[\d*\s]*)[\d*](?!(?:-?[\d*\s]){0,4}(?![\d-*\s]))";
string result = Regex.Replace(input, pattern, "X");
Console.WriteLine(result);
}
Теперь я понимаю это: From INV XXXXXXXX9012 to SAV 0165487
Вот мои рассуждения: Объяснение:
\b
(граница слова) используется для того, чтобы мы начинали и заканчивали словом.
границы для правильной обработки пробелов вокруг цифр.(?<![\d-*])
гарантирует, что перед нами не стоит цифра, тире или звездочка.(?=\d{12,19}\b)
смотрит вперед, чтобы убедиться, что у нас есть от 12 до 19 цифр
за которым следует граница слова.[\d*\s]*
соответствует цифрам, звездочкам или
пространства.[\d*](?!(?:-?[\d*\s]){0,4}(?![\d-*\s]))
соответствует цифре или
звездочку и гарантирует, что за ней не последуют нежелательные символы внутри
определенный диапазон.Если вы просто хотите всегда маскировать, вы можете сделать что-то простое, например:
string pattern = @"\d(?=(?:-?\d){4})";
Причина, по которой ваше текущее регулярное выражение не работает для примера, заключается в том, (?<![\d-*])
цель которого — отделить целое число от текста, но оно просто проверяет один из перечисленных символов. Вместе с [\d*\s]){12,19}
это может соответствовать указанному количеству цифр или пробелов.
Кроме того, я бы не стал использовать что-то вроде [\d-*\s]
. В этом случае (регулярное выражение .NET) ошибки нет, но все равно выглядит некрасиво. Неэкранированный дефис внутри класса символов используется для обозначения диапазона символов. Чтобы соответствовать буквальному дефису, поместите его в начало/конец класса символов или замените его обратной косой чертой.
Согласно вашему следующему вопросу лучше не используйте для этого одно (огромное) регулярное выражение. Конечно, разумнее воспользоваться идеей Виктора, которую легче адаптировать к меняющимся и растущим требованиям.
Номер кредитной карты (совпадает с полным номером)
Проблема заключается в том, чтобы различить копию и номер счета и отделить их от остального текста. Чтобы быть как можно более точным при сопоставлении номера копии (вместо того, чтобы искать 12–19 цифр с пробелами или дефисами где-нибудь между ними), лучше определите номер копии, начав с 3–4 групп, каждая из которых содержит 4 соседние цифры (необязательно). затем следуют 1-3 цифры. Если группы разделены тире или пробелом, зафиксируйте и сопоставьте, насколько разделитель соответствует.
Посмотрите, соответствует ли вам следующий шаблон (демонстрация регулярного выражения).
(?<!\d-?)\d{4}([ -]?)\d{4}(?:\1\d{4}){1,2}(?:\1\d{1,3})?(?!-?\d)
Номер счета (от вашего нового вопроса)
Вы можете использовать то же регулярное выражение для номера счета, просто заменив часть cc на \d{8,9}
. Предполагая, что в учетной записи нет разделителей - нет, как в предоставленных вами образцах (демо регулярного выражения).
(?<!\d-?)\d{8,9}(?!-?\d)
Сочетание обоих узоров
Если это необходимо для вашего приложения, вы также можете объединить оба шаблона в один, чередуя параметры внутри группы без захвата - сначала пробуется более левый вариант (демонстрация регулярного выражения).
(?<!\d-?)(?:\d{4}([ -]?)\d{4}(?:\1\d{4}){1,2}(?:\1\d{1,3})?|\d{8,9})(?!-?\d)
В каждом результирующем совпадении (полные числа) замените, например, \d(?=(?:[ -]?\d){4})
на x
, чтобы замаскировать все цифры, кроме последних 4, с использованием метода, который @WiktorStribiżew предоставляет в своем ответе.
Посмотрите обновленную демо-версию (комбинированный шаблон находится в MaskIfContainCreditCardPanorAcctNumNew
).
Это работает во всех сценариях, кроме «Код счета 3038 — Квитанция № 351461637 221414441406». Здесь «351461637» — номер счета, а «221414441406» — CCno. Теперь он отлично работает для CCno, но для номера учетной записи он все маскирует. Он должен маскировать только последние 4 цифры. Таким образом, это должно выглядеть так: «Код счета 3038 — номер квитанции xxxxx1637 xxxxxxxx1406». Это регулярное выражение, которое я использую (?<!\d)\d(?:[\s-]?\d){7,8}(?!\d)", m => Regex.Replace(m.Value , @"\d(?=(?:[\s-]?\d){4}). Номер счета должен состоять только из 8 или 9 цифр.
Пожалуйста, проигнорируйте регулярное выражение в комментарии выше, поскольку оно предназначалось для ответа Виктора. Я также пробовал использовать это регулярное выражение - Regex.Replace(value, @"(?:\G(?!^)|(?<!\d[ -]?)(?=(?:[ -]?\d ){8,9}(?![ -]?\d)))\d(?=(?:[ -]?\d){4})([ -]?)", "x$1") ;
Я знаю, что это другой сценарий, которого не было в вопросе, но, поскольку все решения есть на этой странице, я не поднимаю новый вопрос,
Я опубликовал другой вопрос для обработки сценариев, связанных с моим последним комментарием. stackoverflow.com/questions/78781466/…. Пожалуйста, посмотрите и предложите.
@user3665818 user3665818 Я еще раз обновил свой ответ (полностью изменился... как и требования). Я надеюсь, что со всеми предоставленными ответами вы сможете как-то решить эту проблему.
Спасибо @bobble bubble. Большое спасибо за вашу помощь, и все сценарии в моей демонстрации отлично работают с вашим ответом. Я знаю, что выдвинул новые требования, но сейчас это должно быть последнее. Мне также нужно заменить звездочку (*) вместе с цифрами в CCNo, если они находятся в диапазоне от 12 до 19.
Таким образом, для строки типа «420657******4405» она должна получить маску «xxxxxxxxxxxx4405». Я использую это регулярное выражение прямо сейчас - Regex.Replace(value, @"(?<!\d-?)\d{4}([ -]?)\d{4}(?:\1\d{4 }){1,2}(?:\1\d{1,3})?(?!-?\d)", m => Regex.Replace(m.Value, @"\d(?=( ?:[ -]?\d){4})", "x"); Такое поведение должно работать и для этого регулярного выражения. Regex.Replace(value, @"(?<!\d-?)(?:\d{4}([ -]?)\d{4}(?:\1\ d{4}){1,2}(?:\1\d{1,3})?|\d{8,9})(?!-?\d)", m => Regex.Replace( m.Value, @"\d(?=(?:[ -]?\d){4})", "x");
Обновленное демо — dotnetfiddle.net/iYPCeo
@user3665818 для этого измените все соответствующие \d
на [\d*]
→ обновленную демо (номер копии и оба номера). Если вы также ожидаете таких, как *****9123-4567
, попробуйте dotnetfiddle.net/7VN03P. Я ушёл из этой темы, рад, что помогло!
прямо сейчас вы маскируете ВСЕ, кроме последних трех цифр, хотя вы заявляете, что хотели бы замаскировать только последние 4. Не могли бы вы предоставить еще пару входных значений и желаемые результаты для каждого из них?