我有这样的功能,通过输入字符串中的正则表达式来识别信用卡,并在没有最后4位数字的情况下屏蔽它:
public CharSequence obfuscate(CharSequence data) {
String[] result = data.toString().replaceAll("[^a-zA-Z0-9-_*]", " ").trim().replaceAll(" +", " ").split(" ");
for(String str : result){
String originalString = str;
String cleanString = str.replaceAll("[-_]","");
CardType cardType = CardType.detect(cleanString);
if(!CardType.UNKNOWN.equals(cardType)){
String maskedReplacement = maskWithoutLast4Digits(cleanString ,replacement);
data = data.toString().replace(originalString , maskedReplacement);
}
}
return data;
}
static String maskWithoutLast4Digits(String input , String replacement) {
if(input.length() < 4){
return input;
}
return input.replaceAll(".(?=.{4})", replacement);
}//模式枚举
public enum CardType {
UNKNOWN,
VISA("^4[0-9]{12}(?:[0-9]{3}){0,2}$"),
MASTERCARD("^(?:5[1-5]|2(?!2([01]|20)|7(2[1-9]|3))[2-7])\\d{14}$"),
AMERICAN_EXPRESS("^3[47][0-9]{13}$"),
DINERS_CLUB("^3(?:0[0-5]|[68][0-9])[0-9]{11}$"),
DISCOVER("^6(?:011|[45][0-9]{2})[0-9]{12}$");
private Pattern pattern;
CardType() {
this.pattern = null;
}
CardType(String pattern) {
this.pattern = Pattern.compile(pattern);
}
public static CardType detect(String cardNumber) {
for (CardType cardType : CardType.values()) {
if (null == cardType.pattern) continue;
if (cardType.pattern.matcher(cardNumber).matches()) return cardType;
}
return UNKNOWN;
}
public Pattern getPattern() {
return pattern;
}
}input1:“有效美国运通卡: 371449635398431”。
output1:“有效美国运通卡:*8431”
input2:“无效信用卡: 1234222222222”//没有任何信用卡模式
output2:“无效信用卡: 1234222222222”
input3:“带有垃圾字符的有效美国运通卡:<3714-4963-5398-431>”
输出:“带有垃圾字符的有效美国运通卡:<***********8431>”
这不是最好的掩蔽方法,因为这个方法将被调用到巨型html中的每个标记和大型文本文件中的每一行--如何提高这个方法的性能。
发布于 2022-03-28 21:06:40
这篇文章完全基于上述答复中的评论,特别是任择议定书中的这一评论:
And also the input string can be "my phone number 12345678 and credit card 1234567890"如果您一心想要从特定的字符串中检索电话号码和信用卡号码,那么可以使用这个RegEx正则表达式:
String regex = String regex = "(\\+?\\d+.{0,1}\\d+.{0,1}\\d+.{0,1}\\d+)|"
+ "(\\+{0,1}\\d+{0,3}\\s{0,1}\\-{0,1}\\({0,1}\\d+" // Phone Numbers
+ "\\){0,1}\\s{0,1}\\-{0,1}\\d+\\s{0,1}\\-{0,1}\\d+)"; // Credit Cards要使用这个regex字符串,您需要通过Pattern/Matcher机制运行它,例如:
String strg = "Valid Phone #: <+1 (212) 555-3456> - "
+ "Valid American Express card 24 with garbage 33.6 characters: <3714-4963-5398-431>";
final java.util.List<String> numbers = new java.util.ArrayList<>();
final String regex = "(\\+?\\d+.{0,1}\\d+.{0,1}\\d+.{0,1}\\d+)|" // Phone Numbers
+ "(\\+{0,1}\\d+{0,3}\\s{0,1}\\-{0,1}\\({0,1}\\d+" // Credit Cards
+ "\\){0,1}\\s{0,1}\\-{0,1}\\d+\\s{0,1}\\-{0,1}\\d+)";
final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(regex); // the regex
final java.util.regex.Matcher matcher = pattern.matcher(strg); // your string
while (matcher.find()) {
numbers.add(matcher.group());
}
for (String str : numbers) {
System.out.println(str);
}有了上面提供的字符串,控制台窗口将显示:
+1 (212) 555-3456
3714-4963-5398-431考虑一下这些原始电话号码和信用卡号子字符串。将这些字符串放入诸如、origPhoneNum、和等回复变量中。现在验证这些数字。您已经在前面的答案中提供了验证信用卡号码的工具。这里有一个验证电话号码的方法:
public static boolean isValidPhoneNumber(String phoneNumber) {
return phoneNumber.matches("^(?!\\b(0)\\1+\\b)(\\+?\\d{1,3}[. -]?)?"
+ "\\(?\\d{3}\\)?([. -]?)\\d{3}\\3\\d{4}$");
}我已经测试了上面提供的regex字符串与来自、许多、不同国家、不同格式的电话号码的关系,并取得了成功。它还测试了多不同的信用卡号码,以许多不同的格式,再次成功。当然,永远不会少一些可能会导致特定问题的格式,因为数据生成源的数字条目显然没有任何规则。
以我在这篇文章顶部所显示的评论为例:
And also the input string can be "my phone number 12345678 and credit card 1234567890"没有办法区分哪个号码应该是电话号码,哪个应该是信用卡号码,除非与字符串中的文本像上面的字符串那样具体地声明为文本。明天或下周可能不会,因为这里似乎没有任何数据输入规则。
字符串表示12345678的电话号码,它是8位数字。该字符串还指示信用卡号码1234567890。在国际上,电话号码可以从9到多达13位数不等,视国家而定。在当地,数字范围的数目将根据国家的不同再次缩小。由于电话号码(国际上)有如此多的数字范围,所以不可能知道被认为是信用卡号码的数字实际上是信用卡号码,除非字符串在数字之前或之后告诉你。如果有的话,它将在下一个输入字符串中。?
为此,我把它留给你来决定如何处理这种情况,但不管它是什么,不要期待它的任何伟大的速度。就像我在之前的回答开始时写的那样:
Wouldn't it be nice if all validations were done before the card numbers
went into the database (or data files).编辑:基于您在前面的答案:下面的最新评论
我做了一个小演示:
// Place this code into a method or event somewhere...
String inputString = "my phone number is +54 123 344-4567 and CC 2222 4053 4324 8877 bla bla bla";
System.out.println("Input: " + inputString);
System.out.println();
final java.util.List<String> numbers = new java.util.ArrayList<>();
final String regex = "(\\+?\\d+.{0,1}\\d+.{0,1}\\d+.{0,1}\\d+)|" // Phone Numbers
+ "(\\+{0,1}\\d+{0,3}\\s{0,1}\\-{0,1}\\({0,1}\\d+" // Credit Cards
+ "\\){0,1}\\s{0,1}\\-{0,1}\\d+\\s{0,1}\\-{0,1}\\d+)";
final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(regex);
final java.util.regex.Matcher matcher = pattern.matcher(inputString);
while (matcher.find()) {
numbers.add(matcher.group());
}
String outputString = inputString;
for (String str : numbers) {
//System.out.println(str); // Uncomment for testing.
// Is substring a valid Phone Number?
int len = str.replaceAll("\\D","").length(); // Crushed number length
if (isValidPhoneNumber(str)) {
outputString = outputString.replace(str, maskAllExceptLast(str, 3, "x"));
}
else if (isValidCreditCardNumber(str)) {
outputString = outputString.replace(str,
maskAllExceptLast(str.replaceAll("\\D",""), 4, "*"));
}
}
System.out.println("Output: " + outputString);支持方法.
public static String maskAllExceptLast (String inputString, int exceptLast_N, String... maskCharacter) {
if(inputString.length() < exceptLast_N){
return inputString;
}
String mask = "*"; // Default mask character.
if (maskCharacter.length > 0) {
mask = maskCharacter[0];
}
return inputString.replaceAll(".(?=.{" + exceptLast_N + "})", mask);
}
/**
* Method to validate a supplied phone number. Currently validates phone
* numbers supplied in the following fashion:
* <pre>
*
* Phone number 1234567890 validation result: true
* Phone number 123-456-7890 validation result: true
* Phone number 123-456-7890 x1234 validation result: true
* Phone number 123-456-7890 ext1234 validation result: true
* Phone number (123)-456-7890 validation result: true
* Phone number 123.456.7890 validation result: true
* Phone number 123 456 7890 validation result: true
* Phone number 01 123 456 7890 validation result: true
* Phone number 1 123-456-7890 validation result: true
* Phone number 1-123-456-7890 validation result: true</pre>
*
* @param phoneNumber (String) The phone number to check.<br>
*
* @return (boolean) True is returned if the supplied phone number is valid.
* False if it isn't.
*/
public static boolean isValidPhoneNumber(String phoneNumber) {
boolean isValid = false;
long len = phoneNumber.replaceAll("\\D","").length(); // Crush the phone Number into only digits
// Check phone Number's length range. Must be from 8 to 12 digits long
if (len < 8 || len > 12) {
return false;
}
// Validate phone numbers of format "xxxxxxxx to xxxxxxxxxxxx"
else if (phoneNumber.matches("\\d+")) {
isValid = true;
}
//validating phone number with -, . or spaces
else if (phoneNumber.matches("^(\\+\\d{1,3}( )?)?((\\(\\d{1,3}\\))|\\d{1,3})[- .]?\\d{3,4}[- .]?\\d{4}$")) {
isValid = true;
}
/* Validating phone number with -, . or spaces and long distance prefix.
This regex also ensures:
- The actual number (withoug LD prefix) should be 10 digits only.
- For North American, numbers with area code may be surrounded
with parentheses ().
- The country code can be 1 to 3 digits long. Optionally may be
preceded by a + sign.
- There may be dashes, spaces, dots or no spaces between country
code, area code and the rest of the number.
- A valid phone number cannot be all zeros. */
else if (phoneNumber.matches("^(?!\\b(0)\\1+\\b)(\\+?\\d{1,3}[. -]?)?"
+ "\\(?\\d{3}\\)?([. -]?)\\d{3}\\3\\d{4}$")) {
isValid = true;
}
//validating phone number with extension length from 3 to 5
else if (phoneNumber.matches("\\d{3}-\\d{3}-\\d{4}\\s(x|(ext))\\d{3,5}")) {
isValid = true;
}
//validating phone number where area code is in braces ()
else if (phoneNumber.matches("^(\\(\\d{1,3}\\)|\\d{1,3})[- .]?\\d{2,4}[- .]?\\d{4}$")) {
isValid = true;
}
//return false if nothing matches the input
else {
isValid = false;
}
return isValid;
}
/**
* Returns true if card (ie: MasterCard, Visa, etc) number is valid using
* the 'Luhn Algorithm'. First this method validates for a correct Card
* Network Number. The supported networks are:<pre>
*
* Number Card Network
* ====================================
* 2 Mastercard (BIN 2-Series) This is NEW!!
* 30, 36, 38, 39 Diners-Club
* 34, 37 American Express
* 35 JBC
* 4 Visa
* 5 Mastercard
* 6 Discovery</pre><br>
*
* Next, the overall Credit Card number is checked with the 'Luhn Algorithm'
* for validity.<br>
*
* @param cardNumber (String)
*
* @return (Boolean) True if valid, false if not.
*/
public static boolean isValidCreditCardNumber(String cardNumber) {
if (cardNumber == null || cardNumber.trim().isEmpty()) {
return false;
}
// Strip card number of all non-digit characters.
cardNumber = cardNumber.replaceAll("\\D", "");
long len = cardNumber.length();
if (len < 14 || len > 16) { // Only going to 16 digits here
return false;
}
// Validate Card Network
String[] cardNetworks = {"2", "30", "34", "35", "36", "37", "38", "39", "4", "5", "6"};
String cardNetNum = cardNumber.substring(0, (cardNumber.startsWith("3") ? 2 : 1));
boolean pass = false;
for (String netNum : cardNetworks) {
if (netNum.equals(cardNetNum)) {
pass = true;
break;
}
}
if (!pass) {
return false; // Invalid Card Network
}
// Validate card number with the 'Luhn algorithm'.
int nDigits = cardNumber.length();
int nSum = 0;
boolean isSecond = false;
for (int i = nDigits - 1; i >= 0; i--) {
int d = cardNumber.charAt(i) - '0';
if (isSecond == true) {
d = d * 2;
}
nSum += d / 10;
nSum += d % 10;
isSecond = !isSecond;
}
return (nSum % 10 == 0);
}上面的代码绝对不会很快!
调整正则表达式或代码,以满足您的特定需求。
发布于 2022-03-25 20:53:02
如果所有的验证都是在之前完成的,那么卡片号就进入了数据库(或数据文件),这不是很好吗?
如果您想要的是速度,那么我不相信在代码的任何部分使用RegEx一定是最好的方法,因为处理正则表达式会消耗大量的时间。例如,以在maskWithoutLast4Digits()方法中执行字符串掩蔽的行为例:
static String maskWithoutLast4Digits(String input, String replacement) {
if(input.length() <= 4){
return input; // There is nothing to mask!
}
return input.replaceAll(".(?=.{4})", replacement);
}并将其替换为以下代码:
static String maskWithoutLast4Digits(String input, String replacement) {
if (input.length() <= 4) {
return input; // There is nothing to mask!
}
char[] chars = input.toCharArray();
Arrays.fill(chars, 0, chars.length - 4, replacement);
return new String(chars);
}您可能会发现,的总体代码将对单个信用卡号字符串执行该任务,其速度几乎是使用正则表达式方法的两倍。这是一个很大的不同。事实上,如果您通过分析器运行代码,您很可能会发现,包含正则表达式的方法对于处理的每个字符串可能会逐渐变慢,而第二个方法将使事情保持更恒定的速度。
不同的信用卡基本上是以一个特定的数字数值开始的,除了几张卡,例如,如果信用卡号从3开始,那么它总是美国运通、餐厅俱乐部或Carte Blanche支付网络的一部分。如果卡以4开头,那么它就是Visa。从5开始的卡号是MasterCards的一部分,而以6开头的卡属于Discover。
Card Starts With No. of Digits
==================================================================
American Express can be 34 or usually 37 15
JBC 35 16
Diners Club usually 36 or can be 38 14
VISA 4 16
Mastercard 5 16
Discovery 6 16您不需要regex来确定信用卡号是否以这些值开头,应该注意的是,如果数字的话,某些信用卡不一定总是包含相同的数字。这可能取决于发卡者,我相信你已经知道了,但无论如何,作为Visa、Mastercard和Discover付款网络的一部分的信用卡有16位数,而属于美国运通支付网络的信用卡只有15位数。虽然信用卡最常见的是16位数,但可能只有13位数,多达19位数。我没有浏览过你的,但我确信它们已经涵盖了这一点(对吧?)。
要删除Regex的使用,可以使用switch/case机制,例如:
// Demo card number...
String cardNumber = "371449635398431";
/* Remove all Characters other than digits.
Don't want them for validation. */
cardNumber = cardNumber.replaceAll("\\D", ""); // Remove all Characters other than digits
String cardName; // Used to store the card's name
switch (cardNumber.substring(0, 1)) {
case "3":
String typeNum = cardNumber.substring(0, 2);
switch(typeNum) {
case "34": case "37":
cardName = "American-Express";
break;
case "35":
cardName = "JBC";
break;
case "30": case "36": case "38": case "39":
cardName = "Diners-Club";
break;
default:
cardName = "UNKNOWN";
}
break;
case "4":
cardName = "Visa";
break;
case "5":
cardName= "Mastercard";
break;
case "6":
cardName = "Discovery";
break;
default:
cardName = "UNKNOWN";
}如果您要在此代码上运行速度测试,而不是迭代一堆RegEx的代码,我相信您会发现considerable的速度有所提高,即使您还想检查每个case中处理的每个卡号的长度。
验证信用卡号码的最佳方法是使用Luhn公式(也称为Luhn Algorithm),它基本上遵循此方案:
当然,如果使用方便,可以将整个过程放入一种方法中,例如:
/**
* Returns true if card (ie: MasterCard, Visa, etc) number is valid using
* the 'Luhn Algorithm'.
*
* @param cardNumber (String)
*
* @return (Boolean)
*/
public static boolean isValidCardNumber(String cardNumber) {
if (cardNumber == null || cardNumber.trim().isEmpty()) {
return false;
}
cardNumber = cardNumber.replaceAll("\\D", "");
// Luhn algorithm
int nDigits = cardNumber.length();
int nSum = 0;
boolean isSecond = false;
for (int i = nDigits - 1; i >= 0; i--) {
int d = cardNumber.charAt(i) - '0';
if (isSecond == true) {
d = d * 2;
}
// We add two digits to handle
// cases that make two digits
// after doubling
nSum += d / 10;
nSum += d % 10;
isSecond = !isSecond;
}
return (nSum % 10 == 0);
}要将所有这些放在一起,您的代码看起来可能与以下内容类似:
public static String validateCreditCardNumber(String cardNumber) {
// Remove all Characters other than digits
cardNumber = cardNumber.replaceAll("\\D", ""); // Remove all Characters other than digits
String cardName; // Used to store the card's name
switch (cardNumber.substring(0, 1)) {
case "3":
String typeNum = cardNumber.substring(0, 2);
switch(typeNum) {
case "34": case "37":
cardName = "American-Express";
break;
case "35":
cardName = "JBC";
break;
case "30": case "36": case "38": case "39":
cardName = "Diners-Club";
break;
default:
cardName = "UNKNOWN";
}
break;
case "4":
cardName = "Visa";
break;
case "5":
cardName= "Mastercard";
break;
case "6":
cardName = "Discovery";
break;
default:
cardName = "UNKNOWN";
}
if (!cardName.equals("UNKNOWN") && isValidCardNumber(cardNumber)) {
return ("The " + cardName + " card number (" + maskWithoutLast4Digits(cardNumber, '*') + ") is VALID!");
}
else {
return ("The " + cardName + " card number (" + maskWithoutLast4Digits(cardNumber, '*') + ") is NOT VALID!");
}
}
public static String maskWithoutLast4Digits (String input, char replacement) {
if (input.length() <= 4) {
return input; // Nothing to mask
}
char[] buf = input.toCharArray();
Arrays.fill(buf, 0, buf.length - 4, replacement);
return new String(buf);
}
/**
* Returns true if card (ie: MasterCard, Visa, etc) number is valid using
* the 'Luhn Algorithm'.
*
* @param cardNumber (String)
*
* @return (Boolean)
*/
public static boolean isValidCardNumber(String cardNumber) {
if (cardNumber == null || cardNumber.trim().isEmpty()) {
return false;
}
cardNumber = cardNumber.replaceAll("\\D", "");
// Luhn algorithm
int nDigits = cardNumber.length();
int nSum = 0;
boolean isSecond = false;
for (int i = nDigits - 1; i >= 0; i--) {
int d = cardNumber.charAt(i) - '0';
if (isSecond == true) {
d = d * 2;
}
// We add two digits to handle
// cases that make two digits
// after doubling
nSum += d / 10;
nSum += d % 10;
isSecond = !isSecond;
}
return (nSum % 10 == 0);
}并且基本上使用上面的内容:
// Demo card number...
String cardNumber = "371449635398431";
String isItValid = validateCreditCardNumber(cardNumber);
System.out.println(isItValid);输出到控制台将是:
The American-Express card number (***********8431) is VALID!我不太清楚您的输出是如何进行的,但是最好在显示它之前将其归档,因为您的速度总是受限于该过程。另外,将数据分解成可管理的块并使用多个executor-Service线程来处理数据将大大提高处理速度,就像使用新的JDK( Java8)之一和利用一些新的方法一样。
https://stackoverflow.com/questions/71615423
复制相似问题