在C#中高效校验16位卡号是否匹配预定义前缀范围的实现方案
在C#中高效校验16位卡号是否匹配预定义前缀范围的实现方案
兄弟,你原来的思路确实踩了个大坑——像那个16位的前缀范围,要生成几百万甚至上千万个字符串存进HashSet,分分钟就内存溢出了!咱们得换个聪明的办法:不用生成所有前缀,直接对卡号的对应长度前缀做数值范围判断,既省内存又高效。
核心思路
每个前缀范围其实都隐含了两个关键信息:前缀的长度,以及该长度下的数值区间。比如945-965就是要求卡号的前3位落在945到965之间,6337461000000000-6337468549999999就是要求整个16位卡号落在这个区间里。
所以我们只需要:
- 提前解析所有前缀范围,记录每个范围的最小值、最大值和前缀长度
- 校验卡号时,遍历每个范围,提取卡号对应长度的前缀,转成数值后判断是否在该范围的区间内
- 只要有一个范围匹配,就返回
true
具体代码实现
首先我们定义一个简单的类来存储每个前缀范围的信息:
private class PrefixRange { public long Min { get; set; } public long Max { get; set; } public int Length { get; set; } }
然后替换原来的字段和方法,改成高效的版本:
private string _ranges = "11220, 11221; 6337461000000000, 6337468549999999; 945, 965;"; private List<PrefixRange> _prefixRanges; private readonly object _rangeLock = new object(); // 初始化前缀范围(只执行一次) private void InitializePrefixRanges() { if (_prefixRanges != null) return; if (string.IsNullOrWhiteSpace(_ranges)) { _prefixRanges = new List<PrefixRange>(); return; } lock (_rangeLock) { // 双重检查锁,确保线程安全且只初始化一次 if (_prefixRanges != null) return; var rangesList = new List<PrefixRange>(); // 拆分每个范围组,忽略空项 var rangeParts = _ranges.Split(';', StringSplitOptions.RemoveEmptyEntries); foreach (var rangeStr in rangeParts) { // 拆分最小值和最大值 var minMaxParts = rangeStr.Split(',', StringSplitOptions.RemoveEmptyEntries); if (minMaxParts.Length != 2) continue; // 尝试解析数值,避免非法输入 if (long.TryParse(minMaxParts[0].Trim(), out var min) && long.TryParse(minMaxParts[1].Trim(), out var max)) { var prefixLength = minMaxParts[0].Trim().Length; // 确保最小值和最大值的前缀长度一致(避免比如123-1234这种不合法的范围) if (max.ToString().Length != prefixLength) continue; rangesList.Add(new PrefixRange { Min = min, Max = max, Length = prefixLength }); } } _prefixRanges = rangesList; } } // 卡号校验方法 public bool IsCardNumberValid(string cardNumber) { // 先校验卡号格式是否符合要求 if (string.IsNullOrWhiteSpace(cardNumber) || cardNumber.Length != 16) { return false; } // 初始化前缀范围(只执行一次) InitializePrefixRanges(); if (_prefixRanges.Count == 0) return false; foreach (var range in _prefixRanges) { // 如果卡号长度小于当前前缀长度,直接跳过(不可能匹配) if (cardNumber.Length < range.Length) continue; // 提取对应长度的前缀 var prefixStr = cardNumber.Substring(0, range.Length); // 转成数值(卡号是纯数字,这里大概率不会失败) if (!long.TryParse(prefixStr, out var prefixValue)) continue; // 判断是否在范围内,匹配到就直接返回true if (prefixValue >= range.Min && prefixValue <= range.Max) { return true; } } // 所有范围都不匹配 return false; }
方案优势
- 内存占用极低:不管范围多大,只存储每个范围的三个数值,完全不会出现内存溢出的问题
- 校验效率高:时间复杂度是O(n),n是前缀范围的数量,遍历速度极快
- 线程安全:用双重检查锁确保初始化过程的线程安全
- 容错性强:自动跳过非法的范围输入,避免程序崩溃
注意事项
- 确保输入的前缀范围是合法的:最小值≤最大值,且最小值和最大值的长度一致(比如不能出现
123-1234这种长度不一致的范围) - 卡号必须是16位纯数字字符串,如果有非数字字符,对应的范围判断会自动跳过
- 如果需要支持更长的卡号(超过16位),可以把
long换成decimal或者BigInteger,避免数值溢出
备注:内容来源于stack exchange,提问作者Bhav
相关产品推荐
相关产品推荐

