【问题标题】:Regex: parsing GitHub usernames (JavaScript)正则表达式:解析 GitHub 用户名 (JavaScript)
【发布时间】:2015-07-28 15:34:00
【问题描述】:

我正在尝试从一段文本中解析 GitHub 用户名(以 @ 开头),以便将它们链接到相关的个人资料。

GitHub 用户名限制为:

  • 带有单个连字符(无连续连字符)的字母数字
  • 不能以连字符开头或结尾(如果以连字符结尾,则将所有内容匹配到那里)
  • 最大长度为 39 个字符。

例如以下文字:

示例@valid hello @valid-username: @another-valid-username, @-invalid @in--valid @ignore-last-dash- an@email.com @another-valid?

脚本...

应该匹配:

  • @有效
  • @有效用户名
  • @another-valid-username
  • @in
  • @ignore-last-dash
  • @another-valid

应该忽略:

  • @-无效
  • an@email.com

通过使用 JavaScript,我已经相当接近了:

/\B@((?!.*(-){2,}.*)[a-z0-9][a-z0-9-]{0,38}[a-z0-9])/ig

但这不匹配具有单个字符(例如@a)的用户名。

这是我迄今为止的测试:https://regex101.com/r/rZ5eW1/2

当前的正则表达式有效吗?以及如何匹配单个非连字符?

【问题讨论】:

  • 嗯...刚想到一个答案,但有一个问题:在@hfd.com的情况下,您希望它做什么?它应该匹配@htd 作为用户名并忽略.com 还是不匹配任何内容?我的是前者。另外,对于@invalid--username,您希望它匹配@invalid 作为用户名,还是忽略整个事情?
  • 感谢您回复我。 @whatever.com 应该匹配 @whatever 所以我想 invalid--username 匹配 invalid 是有意义的
  • 在这种情况下,我的回答应该有效。我不确定是否有可能做类似拒绝匹配的事情,如果它是一个好的匹配,除了一件特定的事情;也许有更复杂的东西,我不知道。
  • 请注意,过去 GitHub 的用户名验证有点松散,并且帐户被取消了(例如,github.com/artur-)。支持人员告诉我,在开头和结尾允许使用连字符,也允许使用下划线。因此,为了消除假阴性(但可能会得到更多的假阳性),您可能需要一个更简单的正则表达式,例如 /\B@[a-z0-9_-]{1,39}/gi

标签: javascript regex github


【解决方案1】:
/\B@([a-z0-9](?:-(?=[a-z0-9])|[a-z0-9]){0,38}(?<=[a-z0-9]))/gi

注意:当此正则表达式遇到用户名中不能出现的一个字符或一组字符(即.--)时,它会匹配从@ 直到该停止点。 OP says that's fine 所以我很喜欢它。因此,如果下划线是匹配区域(不是捕获区域):

@abc.123
@abc--123
@abc-

这通过使用大量嵌套组来工作。 Regex101 has a fantastic breakdown,但这是我的:

  1. \B:这是一个内置的意思是“不是单词边界”,这似乎可以解决问题,但如果 someones.@email.com 之类的东西是有效的电子邮件地址,则可能会出现问题。不过,在这一点上,它与在标点符号后不加空格的人的文本没有区别[1],当他们以@reference 开头句子时。

感谢 Honore Doktorr 提供pointing out that negative lookbehinds don't exist in JS

  1. @:只是文字 @ 符号。为数不多的几个地方之一,一个角色意味着它是什么。
  2. (...):捕获组。它的放置方式意味着它不会捕获@符号,它只会匹配它,因此更容易获取用户名——无需获取子字符串。
  3. [a-z0-9]:匹配任何字母或数字的字符类。由于i 标志,这也匹配大写字母。因为它是第一个字母,它必须出现。
  4. (?:...):这是一个非捕获组。它将一个正则表达式块包装在一个组中而不捕获它。
  5. ...|...我们有两种选择,分别是...
  6. -(?=[a-z0-9]):一个连字符,后跟一个非连字符有效字符。
  7. [a-z0-9]:有效的非连字符。
  8. {0,38}:匹配非捕获组 0 到 38 次(含)。结合#4,这给了我们最多 39 个字母。除此之外的任何内容都将被忽略。
  9. (?&lt;=[a-z0-9]):这是一个积极的后向观察,JS 确实支持。它确保最后一个字符不是-——或者更确切地说,是除连字符之外的有效字符。

这可以通过几种方式“优化”,但老实说,我可能会使用 much 更简单的正则表达式并在事后对其进行一些验证,例如:

// somehow get the prospective username into `user`
if (user.startsWith('-')) { /* reject */ }
if (user.endsWith('-')) { /* reject */ }
if (user.contains('--')) { /* reject */ }

至少解释一下代码中的正则表达式。随意复制粘贴我的信用。

【讨论】:

  • 真的很有帮助。感谢您的详细解释。感谢@honore-doktorr 的提示。
  • 零件大分解。
【解决方案2】:

此表达式也将匹配您的单字用户名。

/\B@(?!.*(-){2,}.*)[a-z0-9](?:[a-z0-9-]{0,37}[a-z0-9])?\b/ig

Sample。说明:

  1. (?!.*(-){2,}.*):您的否定前瞻断言模式的其余部分不能包含两个或更多相邻的破折号。
  2. [a-z0-9]:在@ 之后必须有一个字母数字字符。
  3. (?:[a-z0-9-]{0,37}[a-z0-9])?可能有 0-37 个字母数字字符或破折号,紧跟在 #2 的模式之后的一个字母数字字符 - 或者可能没有,以涵盖单字符用户名。 (?:…) 用于非捕获分组。
  4. \b:整个模式必须以分词符结尾(包括-)。

【讨论】:

  • ...我想我明白了一些。可能是。你能解释一下它是如何工作的吗?
  • 将近一年后回到这个话题让我想起这是多么聪明。干得好!
  • 将这些道具发送给 future-me 非常好,也很聪明——谢谢!
【解决方案3】:

我正在使用我创建的这个简单的RegExgoogle 表单 中获取 github 用户名,它工作得相当不错(有一个非常罕见的警告):

^@\w(-\w|\w\w|\w){0,19}$

地点:

  • ^:行首
  • @-:符号 at 和 dash 本身。
  • \w:[A-Za-z0-9_]、数字、字母(两种情况)和下划线
  • $: 行尾
  • {0,19}:重复前面的括号从零到十九次

总结一下:

  • 匹配的RegEx必须是整行(从^$
  • 它将以@ 开头,后跟字母(两种情况)、数字或下划线(@A@1@_
  • 然后它将遵循重复模式(...){0,19}中的三个选项之一:

    • 破折号和\w(第一个选项)
    • 两个\w(第二个选项)
    • 一个\w(第三个选项)

    这将重复并给出以下模式:

  • 零次:单字母用户名

  • 一次:可以是两个字母的用户名,也可以是三个字母,也可以是三个中间有破折号的字符@w-w
  • 更多次:它保证破折号永远不会出现在开头或结尾,也不会重复,不会出现在其他任何地方。
  • 19 次:如果只使用第一个和第二个选项,则最多给出19*2=38 个字符,加上开头的那个等于39 个字符总数。如果随时使用第三个选项,总大小会更小。

警告:

  • 它无法识别带有@ww-w...w(第三个字母中的破折号,39 个字符)的模式。
  • 虽然它可以识别模式 @ww-w...w 如果大小小于 39 个字符。

问题在于,要实现ww-w,模式被分解为第一个w,然后是单个w,作为重复表达式中的第三个选项(只剩下18个),然后再重复一个w-(第一个选项,只剩下17个),然后,剩下这17个,我们只能得到17*2=34个字符。这意味着,最大值为 38 (34+2+1+1) 个字符,而不是 39 个。

但这对我的目的来说真的没问题,所以如果你需要简单,这里有一个RegEx 可以给你很好的答案。希望翻译成javascript时能帮助你理解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多