Kotlin / 正则表达式 - 用重复字符替换一组模式

3

我想在maskEmail函数中掩盖传入的电子邮件。目前我遇到了一个问题,即当我替换模式中的第2个和第4个组时,星号*没有重复

这是我的代码:

fun maskEmail(email: String): String {
    return email.replace(Regex("(\\w)(\\w*)\\.(\\w)(\\w*)(@.*\\..*)$"), "$1*.$3*$5")
}

以下是输入内容:

tom.cat@email.com
cutie.pie@email.com
captain.america@email.com

这是代码的当前输出结果:
t*.c*@email.com
c*.p*@email.com
c*.a*@email.com

预期输出:

t**.c**@email.com
c****.p**@email.com
c******.a******@email.com

编辑: 我知道这可以很容易地通过for循环完成,但我需要用正则表达式完成。谢谢。


1
如果您的电子邮件不仅包含点和字母呢?例如:my-cutie.pie+here@email.com?我认为在这种情况下,您不能仅依赖单词字符检查。 - Wiktor Stribiżew
2个回答

5

对于你的问题,你需要匹配电子邮件地址中在@符号之前且不是单词首字母的每个字符。你可以使用负向回顾后发断言来匹配单词边界,用正向先行断言来匹配@符号:

(?<!\b)\w(?=.*?@)

匹配的字符可以用*进行替换。
注意在.*上使用懒惰量词(?)以提高效率。 在regex101上演示
注意,正如@CarySwoveland指出的那样,可以将(?<!\b)替换为\B
\B\w(?=.*?@)

在 regex101 上的示例

正如 @Thefourthbird 指出的那样,可以通过将 .*? 替换为 [^\r\n@]* 来进一步提高效率,即:

\B\w(?=[^\r\n@]*@)

这里有一个正则表达式的演示Demo

或者,如果您只需要匹配单个字符串,只需使用[^@]*

\B\w(?=[^@]*@)

在regex101上查看演示


2
太棒了!我会将此设置为答案。供以后参考,这是我们在 Kotlin 中集成模式的方法。fun maskEmail(email: String): String { return email.replace(Regex("(?<!\b)\w(?=.@)"), "") }感谢您,@nick先生! - KennethC
1
@Mandy8055 不好意思,你是对的,它不起作用。我已经把答案改回懒惰了。我确定我曾经看到它工作过,但现在它不行了。机器里出了个幽灵... - Nick
你甚至可以将惰性量词 \B\w(?=[^\r\n@]*@) 替换为否定字符类,以防止一些回溯 https://regex101.com/r/FuBB1i/1 +1 - The fourth bird
1
@Thefourthbird 这很有趣,这会产生多大的差异。我猜如果 OP 只是比较单个字符串,他们可以跳过类中的 \r\n 部分。我会在答案中做出说明。 - Nick

3

我建议保留字符串开头的任意字符以及点加任意字符的组合,并将在@之前跟随除@以外的任意数量字符的任何其他字符替换为*

((?:\.|^).)?.(?=.*@)

将内容替换为$1*。请参见正则表达式演示。这将处理可能包含字符其他于单词(字母/数字/下划线)和.字符的电子邮件。 详细信息
  • ((?:\.|^).)? - 一个可选捕获组,匹配句点或字符串位置并且不是行结束符的任何字符
  • . - 除换行符以外的任何字符...
  • (?=.*@) - 如果后面跟随着除了行结束符之外的0个或多个字符,然后是@
Kotlin代码(使用原始字符串文字来定义正则表达式模式,以避免不必要的反斜杠转义):
fun maskEmail(email: String): String {
    return email.replace(Regex("""((?:\.|^).)?.(?=.*@)"""), "$1*")
}

查看在线 Kotlin 测试

val emails = arrayOf<String>("captain.am-e-r-ica@email.com","my-cutie.pie+here@email.com","tom.cat@email.com","cutie.pie@email.com","captain.america@email.com")
for(email in emails) {    
  val masked = maskEmail(email)
  println("${email}: ${masked}")    
}

输出:

captain.am-e-r-ica@email.com: c******.a*********@email.com
my-cutie.pie+here@email.com: m*******.p*******@email.com
tom.cat@email.com: t**.c**@email.com
cutie.pie@email.com: c****.p**@email.com
captain.america@email.com: c******.a******@email.com

谢谢。我还在思考为什么需要将 ((?:.|^).)? 设为可选项?当我删除该可选模式时,它只显示一个星号。用 (plus) 替换它也是一样的结果。 - KennethC
@KennethC 如果捕获组不是可选的,它将需要一个点/字符串开头,后跟一个点,然后是任何字符。如果您想要替换模式为其等效项 (?:((?:\.|^).)|.)(?=.*@),请参见demo - Wiktor Stribiżew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接