使用正则表达式前瞻将信用卡号拆分为 4 块?
Posted
技术标签:
【中文标题】使用正则表达式前瞻将信用卡号拆分为 4 块?【英文标题】:Split credit card number into 4 chunks using Regex lookahead? 【发布时间】:2017-09-20 02:22:21 【问题描述】:我想将一个信用卡号(在我的情况下我总是有 16 位数字)分成 4 个 4 位数字的块。
我通过积极的展望成功地做到了:
var s="4581458245834584";
var t=Regex.Split(s,"(?=(?:....)*$)");
Console.WriteLine(t);
但我不明白为什么结果是两个填充的空单元格:
我已经知道我可以使用“删除空条目”标志,但我不是在那之后。
但是 - 如果我将正则表达式更改为 (?=(?:....)+$)
,那么我会得到这个结果:
问题
为什么正则表达式会发出空单元格?以及如何修复我的正则表达式,使其首先生成 4 个块(无需“修剪”那些空条目)
【问题讨论】:
卡号不是总是16位吗?假设同样多,不是正则表达式吗?你不能用 rx 或手动根据它进行 aplit 吗? @ChrisWatts 可能有 10 种方法。我想用正则表达式来做。我已经有了拆分解决方案(一条线),所以我已经完成了 95% 的工作。我只需要这 5% 来修复代码。(并理解为什么会发出那些额外的空单元格)。 很公平,只是我的中间想法! 请注意,不同品牌的支付卡使用不同长度的卡号。并非全部都是十六位数。此外,支付卡号的前六位数字构成了识别发卡银行的 IIN(或“BIN”号),因此将这部分分开可能很有用。对于这样一个简单的任务,正则表达式非常昂贵;改为使用简单的子字符串来拆分每个感兴趣的部分。 @Bobulous 它在页面加载时发生一次。它没有运行成千上万次。不应该考虑这种纳米优化(在这个特定任务中)。 【参考方案1】:但我不明白为什么结果是两个填充的空单元格: 让我们尝试分解您的正则表达式。
正则表达式: (?=(?:....)*$)
解释:任何事物的前瞻 (?=) 4 次(?:....) 0 次或多次。只是向前看并且不匹配任何内容将匹配零宽度。
由于您使用的是 *
量词,它表示 零或更多,它匹配开头或字符串以及字符串末尾的第一个零宽度。
从Regex101 Demo 的快照中可视化它
[
那么我怎样才能只选择中间的那 3 个拆分器呢?
我不太了解 C#,但这个 3 步方法可能对你有用。
使用(\d4)
搜索并替换为-\1
。结果将是-4581-4582-4583-4584
。 Demo
现在通过搜索替换第一个 -
为 ^-
。结果将是4581-4582-4583-4584
。 Demo
最后搜索-
并拆分它。 Demo。使用\n
代替演示目的。
受 Royi 回答启发的替代解决方案。
正则表达式: (?=(?!^)(?:\d4)+$)
说明:
(?= // Look ahead for
(?!^) // Not the start of string
(?:\d4)+$ // Multiple group of 4 digits till end of string
)
由于没有匹配任何内容并且仅使用环视断言,因此它将在一组 4 位数字后精确定位零宽度。
Regex101 Demo
【讨论】:
那么我怎样才能只选择中间的那 3 个拆分器? @Royi,不仅要使用前瞻修饰符,还可以另外使用后瞻修饰符来表示任何分割点必须以 4 位数字开头... ;) @elgonzo(旁注)这将使这个 - 对 JS 不可用。 @Royi,如果 JS 兼容性是一个问题,你可以只匹配每 4 个数字而不是匹配分割点,或者这正是挑战? @elgonzo 我不想误导任何人——这就是我写“旁注”的原因。但我更喜欢它对 JS regex 也有效。但是 - 由于这个问题没有用 JS 标记,我应该除了 c# only 解决方案。【参考方案2】:我好像找到了答案。
看看那些分离器 - 我需要摆脱边缘:
所以我想 - 我怎样才能告诉正则表达式引擎“不在行首”?
这正是(?!^)
所做的事情
所以这是新的正则表达式:
var s="4581458245834584";
var t=Regex.Split(s,"(?!^)(?=(?:....)+$)");
Console.WriteLine(t);
结果:
【讨论】:
聪明人。我没有想到要避免一开始就检查。对此仍然有一个解释。 即使(?=(?!^)(?:....)+$) 也有效。困惑如何解释。 @Rahul:您的正则表达式说:查找后跟 4 个字符的内容,这些字符不在输入字符串的开头。更准确地说,您的前瞻模式说:仅当此位置不是输入字符串的开头时,才继续在此位置匹配... @Rahul,抱歉,没注意到 :) 这是一句老话的一个很好的例子,即称一段代码“聪明”是一个巨大的危险信号。如果您知道 - 如问题中所述 - 您正好有 16 位数字并且您想要四个 4 位数字的垃圾,只需使用字符串拆分功能。正则表达式,尤其是正则表达式这样的怪物,对于这项工作来说是一个非常错误的工具。一百万年后我不会让这个代码审查通过。【参考方案3】:嗯,我不知道你为什么需要正则表达式。你只是把事情复杂化了。更好的方法是手动拆分它:
var values = new List<int>();
for(int i =0;i < 4;i++)
var value = int.Parse(s.Substring(i*4, 4));
values.Add(value);
正则表达式解决方案:
var s = "4581458245834584";
var separated = Regex.Match(s, "(.4)4").Groups[1].Captures.Cast<Capture>().Select(x => x.Value).ToArray();
【讨论】:
1 行并没有让它变得更好。您只需将复杂性放入正则表达式查询中。您将模式数学用于简单的分离问题,您可以从一开始就处理更少的血液和更有效的解决方案。如果您只是选择最简单的路径,问题本身就不会出现。 嗯,我认为上下文是用正则表达式标签设置的。 好吧,除非你处理整个支付卡语法 - en.wikipedia.org/wiki/Payment_card_number 我认为这个解决方案很复杂而且根本无法调试。 我会删除这条不相关的评论“如果你对正则表达式如此着迷”。这不专业,与问题无关。 仅供参考:您可以稍微简化一下您的正则表达式:Regex.Matches(s, ".4").Cast<Match>().Select(m => m.Value).ToArray()
... 现在,.4
不是一个很棒的正则表达式... :-P【参考方案4】:
已经提到*
量词也匹配在前面有零组匹配的字符串末尾。为避免在开头和结尾匹配,您可以使用\B
non word boundary,它只匹配两个单词字符,不匹配开头和结尾。
\B(?=(?:.4)+$)
See demo at regex101
因为在字符串的开头或结尾不会触发前瞻,你甚至可以use *
【讨论】:
以上是关于使用正则表达式前瞻将信用卡号拆分为 4 块?的主要内容,如果未能解决你的问题,请参考以下文章