diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d327c6..aabec00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 0.3 -- +- Add Utility classes for regex `RegexUtils`, ID generation `NanoIdGenerator` `SnowflakeGenerator` ## 0.2 diff --git a/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/id/NanoIdGenerator.java b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/id/NanoIdGenerator.java new file mode 100644 index 0000000..eaffb88 --- /dev/null +++ b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/id/NanoIdGenerator.java @@ -0,0 +1,56 @@ +package space.x9x.radp.commons.id; + +import lombok.experimental.UtilityClass; + +import java.security.SecureRandom; +import java.util.Random; + +/** + * @author x9x + * @since 2024-12-27 12:08 + */ +@UtilityClass +public class NanoIdGenerator { + + public static final SecureRandom DEFAULT_NUMBER_GENERATOR = new SecureRandom(); + private static final char[] DEFAULT_ALPHABET = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); + public static final int DEFAULT_SIZE = 21; + + public static String randomNanoId() { + return randomNanoId(DEFAULT_NUMBER_GENERATOR, DEFAULT_ALPHABET, DEFAULT_SIZE); + } + + @SuppressWarnings("t") + public static String randomNanoId(final Random random, final char[] alphabet, final int size) { + if (random == null) { + throw new IllegalArgumentException("random cannot be null."); + } + if (alphabet == null) { + throw new IllegalArgumentException("alphabet cannot be null."); + } + if (alphabet.length == 0 || alphabet.length >= 256) { + throw new IllegalArgumentException("alphabet must contain between 1 and 255 symbols."); + } + if (size <= 0) { + throw new IllegalArgumentException("size must be greater than zero."); + } + + final int mask = (2 << (int) Math.floor(Math.log(alphabet.length - 1) / Math.log(2))) - 1; + final int step = (int) Math.ceil(1.6 * mask * size / alphabet.length); + + final StringBuilder idBuilder = new StringBuilder(); + while (true) { + final byte[] bytes = new byte[step]; + random.nextBytes(bytes); + for (int i = 0; i < step; i++) { + final int alphabetIndex = bytes[i] & mask; + if (alphabetIndex < alphabet.length) { + idBuilder.append(alphabet[alphabetIndex]); + if (idBuilder.length() == size) { + return idBuilder.toString(); + } + } + } + } + } +} diff --git a/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/id/SnowflakeGenerator.java b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/id/SnowflakeGenerator.java new file mode 100644 index 0000000..ed941ae --- /dev/null +++ b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/id/SnowflakeGenerator.java @@ -0,0 +1,196 @@ +package space.x9x.radp.commons.id; + +import lombok.Builder; +import lombok.NoArgsConstructor; +import lombok.Synchronized; + +/** + * Twitter 雪花算法生成器 + *

+ * 共 64 位, 0 + 41 位时间戳 + 10 位机器 ID(5 位机器 ID + 5 位数据中心 ID)+ 12 位序列号 + *

+ * ┌─────────────── 41 bits ────────────────┐┌──── 10 bits ────┐┌─ 12 bits ─┐
+ * │         时间戳(毫秒数) offset            ││数据中心ID+机器ID  ││  序列号    │
+ * └────────────────────────────────────────┘└─────────────────┘└───────────┘
+ * 
+ *
{@code
+ * public class SnowflakeDemo {
+ *     public static void main(String[] args) {
+ *         // 1) 直接 new 一个默认的 SnowflakeGenerator(dataCenterId=1, workerId=1)
+ *         SnowflakeGenerator generator = new SnowflakeGenerator();
+ *
+ *         // 2) 调用 nextId() 方法生成 Snowflake ID
+ *         long id = generator.nextId();
+ *         System.out.println("Snowflake ID: " + id);
+ *
+ *         // 3) 使用静态方法一次生成
+ *         long staticId = SnowflakeGenerator.nextId(2, 3);
+ *         System.out.println("Snowflake ID (static): " + staticId);
+ *     }
+ * }
+ * }
+ * + * @author x9x + * @since 2024-12-27 12:36 + */ +@NoArgsConstructor +public class SnowflakeGenerator { + + /** + * 开始时间截(2018-01-01) + */ + private final long twepoch = 1514736000000L; + + /** + * 机器 ID 占 5 位(机器 ID 和数据中心 ID 加起来不能超过 10) + */ + private final long workerIdBits = 5L; + + /** + * 数据中心 ID 占 5 位 + */ + private final long datacenterIdBits = 5L; + + /** + * 序列号占 12 位 + */ + private final long sequenceBits = 12L; + + /** + * 支持的最大机器 ID + */ + private final long maxWorkerId = -1L ^ (-1L << workerIdBits); + + /** + * 支持的最大数据标识 ID + */ + private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); + + /** + * 机器 ID 向左移 12 位 + */ + private final long workerIdShift = sequenceBits; + + /** + * 数据标识 ID 向左移 17 位 + */ + private final long datacenterIdShift = sequenceBits + workerIdBits; + + /** + * 时间截向左移 22 位 + */ + private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + + /** + * 生成序列的掩码 + */ + private final long sequenceMask = -1L ^ (-1L << sequenceBits); + + /** + * 机器 ID + */ + private long workerId = 1L; + + /** + * 数据中心 ID + */ + private long dataCenterId = 1L; + + /** + * 毫秒内序列号 + */ + private long sequence = 0L; + + /** + * 上次生成 ID 的时间截 + */ + private long lastTimestamp = -1L; + + /** + * 构造函数 + * + * @param dataCenterId 数据中心 ID + * @param workerId 工作 ID + */ + @Builder + public SnowflakeGenerator(long dataCenterId, long workerId) { + if (dataCenterId > maxDatacenterId || dataCenterId < 0) { + throw new IllegalArgumentException(String.format("dataCenterId 不能大于 %d 或者小于 0", maxDatacenterId)); + } + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format("workerId 不能大于 %d 或者小于 0", maxWorkerId)); + } + this.dataCenterId = dataCenterId; + this.workerId = workerId; + } + + /** + * 获得下一个 ID + * + * @param dataCenterId 数据中心 ID + * @param workerId 工作 ID + * @return snowflakeId + */ + public static long nextId(long dataCenterId, long workerId) { + return SnowflakeGenerator.builder().dataCenterId(dataCenterId).workerId(workerId).build().nextId(); + } + + /** + * 获得下一个 ID + * + * @return snowflakeId + */ + @Synchronized + public long nextId() { + long timestamp = timeGen(); + + // 如果当前时间小于上一次 ID 生成的时间戳,说明系统时钟回退过,抛出异常 + if (timestamp < lastTimestamp) { + throw new RuntimeException(String.format("时钟被回退 %d 毫秒,无法生成", lastTimestamp - timestamp)); + } + + // 如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & sequenceMask; + // 毫秒内序列溢出 + if (sequence == 0) { + // 阻塞到下一个毫秒,获得新的时间戳 + timestamp = tillNextMillis(lastTimestamp); + } + } else { // 时间戳改变,毫秒内序列重置 + sequence = 0L; + } + + // 上次生成 ID 的时间截 + lastTimestamp = timestamp; + + // 移位并通过或运算拼到一起组成 64 位的 ID + return ((timestamp - twepoch) << timestampLeftShift) + | (dataCenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * + * @param lastTimestamp 上次生成 ID 的时间截 + * @return 当前时间戳 + */ + protected long tillNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间戳 + */ + protected long timeGen() { + return System.currentTimeMillis(); + } +} diff --git a/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/regex/RegexUtils.java b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/regex/RegexUtils.java new file mode 100644 index 0000000..2de3a87 --- /dev/null +++ b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/regex/RegexUtils.java @@ -0,0 +1,85 @@ +package space.x9x.radp.commons.regex; + +import lombok.NonNull; +import lombok.experimental.UtilityClass; +import space.x9x.radp.commons.regex.pattern.RegexCache; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

使用示例:

+ *
{@code
+ * public class Demo {
+ *     public static void main(String[] args) {
+ *         // 1) 判断整个字符串是否匹配正则
+ *         boolean isMatch = RegexUtils.isMatch("\\d+", "12345");
+ *         System.out.println(isMatch); // true
+ *
+ *         // 2) 查找目标字符串中是否存在匹配
+ *         boolean found = RegexUtils.find("abc", "xxx abc yyy");
+ *         System.out.println(found); // true
+ *
+ *         // 3) 分组匹配示例
+ *         List groups = RegexUtils.group("([a-z]+)", "abc def ghi");
+ *         System.out.println(groups); // [abc, def, ghi]
+ *
+ *         // 4) 替换所有匹配项
+ *         String replaced = RegexUtils.replaceAll("\\s+", "abc def", "_");
+ *         System.out.println(replaced); // "abc_def"
+ *     }
+ * }
+ * }
+ * + * @author x9x + * @see space.x9x.radp.commons.regex.pattern.Regex + * @since 2024-12-27 12:50 + */ +@UtilityClass +public class RegexUtils { + + public static boolean isMatch(@NonNull String regex, @NonNull CharSequence input) { + return Pattern.matches(regex, input); + } + + public static boolean find(@NonNull String regex, @NonNull CharSequence input) { + Pattern pattern = RegexCache.get(regex, Pattern.CASE_INSENSITIVE); + return pattern.matcher(input).find(); + } + + public static List group(@NonNull String regex, @NonNull CharSequence input) { + List matches = new ArrayList<>(); + Pattern pattern = RegexCache.get(regex, Pattern.DOTALL); + Matcher matcher = pattern.matcher(input); + int i = 1; + while (matcher.find()) { + matches.add(matcher.group(i)); + i++; + } + return matches; + } + + public static String groupFirst(@NonNull String regex, @NonNull CharSequence input) { + return group(regex, input, 1); + } + + public static String group(@NonNull String regex, @NonNull CharSequence input, int groupIndex) { + Pattern pattern = RegexCache.get(regex, Pattern.DOTALL); + Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + return matcher.group(groupIndex); + } + return null; + } + + public static String replaceAll(@NonNull String regex, @NonNull CharSequence input, @NonNull String replacement) { + return Pattern.compile(regex).matcher(input).replaceAll(replacement); + } + + public static String replaceFirst(@NonNull String regex, @NonNull CharSequence input, @NonNull String replacement) { + Pattern pattern = RegexCache.get(regex, Pattern.DOTALL); + return pattern.matcher(input).replaceFirst(replacement); + } +} diff --git a/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/regex/pattern/Regex.java b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/regex/pattern/Regex.java new file mode 100644 index 0000000..aec7319 --- /dev/null +++ b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/regex/pattern/Regex.java @@ -0,0 +1,218 @@ +package space.x9x.radp.commons.regex.pattern; + +import lombok.experimental.UtilityClass; + +/** + * 常用正则表达式 + * + * @author x9x + * @since 2024-12-27 12:50 + */ +@UtilityClass +public class Regex { + + + /** + * 字母 + */ + public static final String WORD = "[a-zA-Z]+"; + + /** + * 汉字 + */ + public static final String CHINESE = "^[\\u4e00-\\u9fa5]+$"; + + /** + * 用户名:取值范围为 a-z、A-Z、0-9、"_"、汉字,不能以"_"结尾,长度在 1~18 区间 + */ + public static final String USERNAME = "^[\\w\\u4e00-\\u9fa5]{1,18}(?中国大陆:+86 Mainland China + *
中国香港:+852 Hong Kong + *
中国澳门:+853 Macao + *
中国台湾:+886 Taiwan + */ + public static final String MOBILE_HK = "(?:0|852|\\+852)?\\d{8}"; + + /** + * 中国台湾移动电话 + *
中国台湾:+886 Taiwan + */ + public static final String MOBILE_TW = "(?:0|886|\\+886)?(?:|-)09\\d{8}"; + + /** + * 中国澳门移动电话 + *
中国澳门 +853 Macao + */ + public static final String MOBILE_MO = "(?:0|853|\\+853)?(?:|-)6\\d{7}"; + + /** + * 座机号码 + */ + public static final String TELEPHONE = "(010|02\\d|0[3-9]\\d{2})-?(\\d{6,8})"; + + /** + * 座机号码 +400/+800 + */ + public static final String TELEPHONE_400_800 = "0\\d{2,3}[\\- ]?[1-9]\\d{6,7}|[48]00[\\- ]?[1-9]\\d{6}"; + + /** + * 身份证号码(15 位) + */ + public static final String ID_CARD_15 = "^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$"; + + /** + * 身份证号码(18 位) + */ + public static final String ID_CARD_18 = "[1-9]\\d{5}[1-2]\\d{3}((0\\d)|(1[0-2]))(([012]\\d)|3[0-1])\\d{3}(\\d|X|x)"; + + /** + * 社会统一信用代码 + */ + public static final String CREDIT_CODE = "^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$"; + + /** + * 邮编号码 + */ + public static final String ZIP_CODE = "^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[0-7]|6[0-7]|7[0-5]|8[0-9]|9[0-8])\\d{4}|99907[78]$"; + + /** + * 生日 + */ + public static final String BIRTHDAY = "^(\\d{2,4})([/\\-.年]?)(\\d{1,2})([/\\-.月]?)(\\d{1,2})日?$"; + + /** + * QQ号码 + */ + public static final String TENCENT_QQ = "[1-9][0-9]{4,}"; + + /** + * 车牌号码(中国) + */ + public static final String PLATE_NUMBER_CN = "^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领]" + + "[A-Z](([0-9]{5}[ABCDEFGHJK])|([ABCDEFGHJK]([A-HJ-NP-Z0-9])[0-9]{4})))|" + + "([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领]\\d{3}\\d{1,3}[领])|" + + "([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$"; + + /** + * 车架号 + */ + public static final String CAR_VIN = "^[A-Za-z0-9]{17}$"; + + /** + * 驾驶证 + */ + public static final String CAR_DRIVING_LICENCE = "^[0-9]{12}$"; + + /** + * IPv4 + */ + public static final String IPV4 = "\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)" + + "\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)" + + "\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b"; + + /** + * IPv6 + */ + public static final String IPV6 = "(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|" + + "([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" + + "([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" + + "([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:" + + "((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?" + + "((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:)" + + "{1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))"; + + /** + * URL + */ + public static final String URL = "[a-zA-z]+://[^\\s]*"; + + /** + * HTTP + */ + public static final String HTTP = "(https://|http://)?([\\w-]+\\.)+[\\w-]+(:\\d+)*(/[\\w- ./?%&=]*)?"; + + /** + * 货币 + */ + public static final String MONEY = "^(\\d+(?:\\.\\d+)?)$"; + + /** + * 日期 + */ + public static final String DATE = "^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|" + + "(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|" + + "(?:0[48]|[2468][048]|[13579][26])00)-02-29)$"; + + /** + * 时间 + */ + public static final String TIME = "\\d{1,2}:\\d{1,2}(:\\d{1,2})?"; + + /** + * UUID + */ + public static final String UUID = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"; + + /** + * MAC + */ + public static final String MAC_ADDRESS = "((?:[a-fA-F0-9]{1,2}[:-]){5}[a-fA-F0-9]{1,2})|0x(\\d{12}).+ETHER"; + + /** + * 十六进制字符串 + */ + public static final String HEX = "^[a-fA-F0-9]+$"; + + /** + * 整数 + */ + public static final String INTEGER = "^-?[1-9]\\d*$"; + + /** + * 正整数 + */ + public static final String INTEGER_POSITIVE = "^[1-9]\\d*$"; + + /** + * 非正整数 + */ + public static final String INTEGER_POSITIVE_REVERSE = "^-[1-9]\\d*|0$"; + + /** + * 负整数 + */ + public static final String INTEGER_NEGATIVE = "^-[1-9]\\d*$"; + + /** + * 非负整数 + */ + public static final String INTEGER_NEGATIVE_REVERSE = "^[1-9]\\d*|0$"; + + /** + * 浮点数 + */ + public static final String FLOAT = "^-?[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*$"; + + /** + * 正浮点数 + */ + public static final String FLOAT_POSITIVE = "^[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*$"; + + /** + * 负浮点数 + */ + public static final String FLOAT_NEGATIVE = "^-[1-9]\\d*\\.\\d*|-0\\.\\d*[1-9]\\d*$"; +} diff --git a/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/regex/pattern/RegexCache.java b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/regex/pattern/RegexCache.java new file mode 100644 index 0000000..6f14fdf --- /dev/null +++ b/radp-components/radp-commons/src/main/java/space/x9x/radp/commons/regex/pattern/RegexCache.java @@ -0,0 +1,246 @@ +package space.x9x.radp.commons.regex.pattern; + +import lombok.experimental.UtilityClass; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +/** + * 正则表达式缓存 + * + * @author x9x + * @since 2024-12-27 12:55 + */ +@UtilityClass +public class RegexCache { + + + /** + * 字母 + */ + public static final Pattern WORD = Pattern.compile(Regex.WORD); + + /** + * 汉字 + */ + public static final Pattern CHINESE = Pattern.compile(Regex.CHINESE); + + /** + * 用户名:取值范围为 a-z、A-Z、0-9、"_"、汉字,不能以"_"结尾,长度在 1~18 区间 + */ + public static final Pattern USERNAME = Pattern.compile(Regex.USERNAME); + + /** + * 邮箱,符合RFC 5322规范 + */ + public static final Pattern EMAIL = Pattern.compile(Regex.EMAIL); + + /** + * 移动电话 + */ + public static final Pattern MOBILE = Pattern.compile(Regex.MOBILE); + + /** + * 中国香港移动电话 + *
中国大陆:+86 Mainland China + *
中国香港:+852 Hong Kong + *
中国澳门:+853 Macao + *
中国台湾:+886 Taiwan + */ + public static final Pattern MOBILE_HK = Pattern.compile(Regex.MOBILE_HK); + + /** + * 中国台湾移动电话 + *
中国台湾:+886 Taiwan + */ + public static final Pattern MOBILE_TW = Pattern.compile(Regex.MOBILE_TW); + + /** + * 中国澳门移动电话 + *
中国澳门 +853 Macao + */ + public static final Pattern MOBILE_MO = Pattern.compile(Regex.MOBILE_MO); + + /** + * 座机号码 + */ + public static final Pattern TELEPHONE = Pattern.compile(Regex.TELEPHONE); + + /** + * 座机号码 +400/+800 + */ + public static final Pattern TELEPHONE_400_800 = Pattern.compile(Regex.TELEPHONE_400_800); + + /** + * 身份证号码(15 位) + */ + public static final Pattern ID_CARD_15 = Pattern.compile(Regex.ID_CARD_15); + + /** + * 身份证号码(18 位) + */ + public static final Pattern ID_CARD_18 = Pattern.compile(Regex.ID_CARD_18); + + /** + * 社会统一信用代码 + */ + public static final Pattern CREDIT_CODE = Pattern.compile(Regex.CREDIT_CODE); + + /** + * 邮编号码 + */ + public static final Pattern ZIP_CODE = Pattern.compile(Regex.ZIP_CODE); + + /** + * 生日 + */ + public static final Pattern BIRTHDAY = Pattern.compile(Regex.BIRTHDAY); + + /** + * QQ号码 + */ + public static final Pattern TENCENT_QQ = Pattern.compile(Regex.TENCENT_QQ); + + /** + * 车牌号码(中国) + */ + public static final Pattern PLATE_NUMBER_CN = Pattern.compile(Regex.PLATE_NUMBER_CN); + + /** + * 车架号 + */ + public static final Pattern CAR_VIN = Pattern.compile(Regex.CAR_VIN); + + /** + * 驾驶证 + */ + public static final Pattern CAR_DRIVING_LICENCE = Pattern.compile(Regex.CAR_DRIVING_LICENCE); + + /** + * IPv4 + */ + public static final Pattern IPV4 = Pattern.compile(Regex.IPV4); + + /** + * IPv6 + */ + public static final Pattern IPV6 = Pattern.compile(Regex.IPV6); + + /** + * URL + */ + public static final Pattern URL = Pattern.compile(Regex.URL); + + /** + * HTTP + */ + public static final Pattern HTTP = Pattern.compile(Regex.HTTP); + + /** + * 货币 + */ + public static final Pattern MONEY = Pattern.compile(Regex.MONEY); + + /** + * 日期 + */ + public static final Pattern DATE = Pattern.compile(Regex.DATE); + + /** + * 时间 + */ + public static final Pattern TIME = Pattern.compile(Regex.TIME); + + /** + * UUID + */ + public static final Pattern UUID = Pattern.compile(Regex.UUID); + + /** + * MAC + */ + public static final Pattern MAC_ADDRESS = Pattern.compile(Regex.MAC_ADDRESS); + + /** + * 十六进制字符串 + */ + public static final Pattern HEX = Pattern.compile(Regex.HEX); + + /** + * 整数 + */ + public static final Pattern INTEGER = Pattern.compile(Regex.INTEGER); + + /** + * 正整数 + */ + public static final Pattern INTEGER_POSITIVE = Pattern.compile(Regex.INTEGER_POSITIVE); + + /** + * 非正整数 + */ + public static final Pattern INTEGER_POSITIVE_REVERSE = Pattern.compile(Regex.INTEGER_POSITIVE_REVERSE); + + /** + * 负整数 + */ + public static final Pattern INTEGER_NEGATIVE = Pattern.compile(Regex.INTEGER_NEGATIVE); + + /** + * 非负整数 + */ + public static final Pattern INTEGER_NEGATIVE_REVERSE = Pattern.compile(Regex.INTEGER_NEGATIVE_REVERSE); + + /** + * 浮点数 + */ + public static final Pattern FLOAT = Pattern.compile(Regex.FLOAT); + + /** + * 正浮点数 + */ + public static final Pattern FLOAT_POSITIVE = Pattern.compile(Regex.FLOAT_POSITIVE); + + /** + * 负浮点数 + */ + public static final Pattern FLOAT_NEGATIVE = Pattern.compile(Regex.FLOAT_NEGATIVE); + + /** + * 正则编译缓存 + */ + private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); + + /** + * 获取缓存,如果获取不到,先编译后存入缓存 + * + * @param regex 正则字符串 + * @param flags 匹配规则 + * @return 正则表达式 + */ + public static Pattern get(String regex, int flags) { + Pattern pattern = CACHE.get(regex); + if (pattern == null) { + pattern = Pattern.compile(regex, flags); + CACHE.put(regex, pattern); + } + return pattern; + } + + /** + * 移除缓存 + * + * @param regex 正则字符串 + * @return 正则表达式 + */ + public static Pattern remove(String regex) { + return CACHE.remove(regex); + } + + /** + * 清空缓存 + */ + public static void clear() { + CACHE.clear(); + } +}