正则表达式:匹配包含数字和字母但不包含纯数字字符串的字符串

3

问题

我想使用一个正则表达式(如果可能的话)来要求一个字符串符合 [A-Za-z0-9_],但不允许出现以下情况:

  • 只包含数字或符号的字符串。
  • 以符号开头或结尾的字符串
  • 相邻的多个符号

有效的

  • test_0123
  • t0e1s2t3
  • 0123_test
  • te0_s1t23
  • t_t

无效的

  • t__t
  • ____
  • 01230123
  • _0123
  • _test
  • _test123
  • test_
  • test123_

规则原因

这是为了过滤我正在开发的网站的用户名。我根据特定的原因制定了这些规则。

  • 只有数字和/或符号的用户名可能会导致路由和数据库查找方面的问题。路由 /users/#{id} 允许 id 既可以是用户的ID,也可以是用户的名称。因此,名称和ID不应该发生冲突。

  • _test 看起来很奇怪,我不认为它是有效的子域名,例如 _test.example.com

  • 我不喜欢 t__t 作为子域名的样子,例如 t__t.example.com


根据下面的评论,我认为你正在使用Ruby(可能是Ruby 1.8?)。无论如何,请明确说明你正在使用什么语言和框架(Rails?)以及它们的版本。 - Telemachus
1
无论您选择哪种解决方案,我强烈建议使用这些正面和负面案例创建单元测试,以确保正则表达式正常工作,并且不会被后来的修改破坏。 - Rafe
epochwolf,你实际上不需要使用lookbehinds来使用单个正则表达式 - 请参见我的更新答案。 - Amber
@Dav,我回滚了我的编辑并接受了你的答案。 - epochwolf
有人可能会认为,一个过于复杂的方法比一个简洁的方法更不有帮助,并且可能比不使用正则表达式更不有帮助。我并不是说这一点一定会导致投票下降,但我可以理解背后的道理。 - Amber
显示剩余2条评论
9个回答

8
这完全符合你想要的内容:
/\A(?!_)(?:[a-z0-9]_?)*[a-z](?:_?[a-z0-9])*(?<!_)\z/i
  1. 至少有一个字母字符(中间的[a-z])。
  2. 不以下划线开头或结尾(开头和结尾的(?!_)(?<!_))。
  3. 在字母字符之前和之后可能有任意数量的数字、字母或下划线,但每个下划线必须至少与一个数字或字母分隔(其余部分)。

编辑:实际上,由于正则表达式的其余部分如何工作,您可能甚至不需要前瞻/后顾 - 第一个?:括号不会允许下划线,直到出现一个字母数字字符,第二个?:括号只有在它之前有一个字母数字字符时才允许下划线:

/\A(?:[a-z0-9]_?)*[a-z](?:_?[a-z0-9])*\z/i

应该可以正常工作。

看起来很有前途,哪种正则表达式可以使用 (?<!_) - Sinan Taifour
然而,在这种情况下,您可能实际上可以省略正则表达式中的反向引用 - /^(?:[a-z0-9]_?)*[a-z](?:_?[a-z0-9])*$/i 应该可以很好地工作。 - Amber
第二个版本是否检查初始字符不是下划线? - Telemachus
是的 - 初始下划线将不匹配 (?:[a-z0-9]_?)*[a-z],因此会被拒绝。 - Amber
我非常喜欢这个正则表达式,但是看起来在 Ruby 4.0 中它似乎不再(up to snuff)[https://github.com/rails/rails/pull/6569]。^ 和 $ 显然是一个非常真实的安全威胁。 - nullnullnull
显示剩余6条评论

2

关于:

/^(?=[^_])([A-Za-z0-9]+_?)*[A-Za-z](_?[A-Za-z0-9]+)*$/

它不使用后向引用。

编辑:

适用于您所有的测试用例。与 Ruby 兼容。


2
我相信你可以将所有内容放入一个正则表达式中,但这并不简单,而且我不确定为什么要坚持使用一个正则表达式。为什么不在验证过程中使用多个步骤?如果在用户创建新帐户时进行验证检查,那么没有必要尝试将其压缩到一个正则表达式中。(也就是说,您一次只处理一个项目,而不是数百或数千个。对于普通大小的用户名,几次通过应该需要很少的时间,我想。)
首先,如果名称中不含至少一个数字,则拒绝;然后,如果名称中不含至少一个字母,则拒绝;然后检查起始和结束是否正确等。每个步骤都可以是易于阅读和维护的简单正则表达式。

validates_format_of :name, :with => /\A[A-Za-z0-9-_]{4,20}\Z/, :on => :create 我正在尝试重新学习正则表达式。已经有一段时间没有真正处理过困难的正则表达式了。(这是一个个人项目,不是工作相关的项目,否则我会使用多个正则表达式解决方案) - epochwolf
-1 因为问题要求使用正则表达式而不是另一种解决方案。根据我的看法,实现上述功能的正则表达式非常简单,因此没有必要使用其他解决方案。 - Rado
3
@Rado:你有自己的意见(和投票权),但是我认为这里失败的答案记录清楚表明正则表达式在这里并不够简单易用。作为一个更普遍的规则,我认为有时候说“你可以这样做,但也许这不是最好的选择”是合理的。 - Telemachus
@Telemachus:我花了不到5分钟就想出了一个可行的解决方案(虽然它可以再缩短一点)。这是最适合使用正则表达式解决的问题,我不确定为什么有人会因为“可能”需要更长时间来快速实现而避免使用正则表达式。 - Rado
1
/^(?:[a-z0-9]_?)*[a-z](?:_?[a-z0-9])*$/i 在正则表达式中相当简单(实际上,如果不关心不生成捕获,则 ?: 前缀是可选的)。虽然有时候正则表达式并不是最好的选择,但我认为只要花一点时间考虑问题,这并不是其中之一。 - Amber
@Rado:我并没有考虑实现时间,而是考虑维护的便利性。像这样试图一次性完成四五件事情的复杂正则表达式很难完全正确和阅读。已经有许多好的工作答案了,所以也许我在这里有些着急,但总的来说,我会选择更清洁、更易于维护的正则表达式,而不是一个单一的怪物正则表达式。也许这意味着我应该在我的正则表达式技能上下功夫。 - Telemachus

1

这不会阻止“__”,但它会获取其余部分:

([A-Za-z]|[0-9][0-9_]*)([A-Za-z0-9]|_[A-Za-z0-9])*

这里是获取所有规则的更长表单:

([A-Za-z]|([0-9]+(_[0-9]+)*([A-Za-z|_[A-Za-z])))([A-Za-z0-9]|_[A-Za-z0-9])*

哎呀,这太丑了。我同意Telemachus的观点,即使从技术上讲可以用一个正则表达式完成,你也不应该这样做。正则表达式通常很难维护。


1
问题要求一个单一的正则表达式,并暗示它应该是一个可以匹配的正则表达式,这很好,已经有其他人回答了。但是出于兴趣,我注意到这些规则更容易直接陈述为一个不应该匹配的正则表达式。即:
x !~ /[^A-Za-z0-9_]|^_|_$|__|^\d+$/
  • 不能包含除字母、数字和_之外的字符
  • 不能以_开头
  • 不能以_结尾
  • 不能有两个连续的_
  • 不能全部是数字

在Rails的validates_format_of中无法使用这种方式,但您可以将其放入类的验证方法中,我认为您仍然有很好的机会在一个月或一年后理解您的意思。


这是一个非常好的观点。实际上,我已经将我的整个问题和我接受的答案添加到了正则表达式出现的文件底部。还有一条关于正则表达式的注释,请查看文件底部。因为我知道以后会乐在其中地尝试解决正则表达式。 - epochwolf

0
(?=.*[a-zA-Z].*)^[A-Za-z0-9](_?[A-Za-z0-9]+)*$

这个可以工作。

向前查看以确保字符串中至少有一个字母,然后开始消耗输入。每次出现下划线时,下一个下划线之前必须有一个数字或字母。


1
这会排除以数字结尾的名称,不是吗?楼主认为 test_0123 是有效的。 - Telemachus
不允许使用 te_t0123test - epochwolf
好的。那很容易改变。 - Welbog
这个不匹配像 "hello_t"(以单个字符结尾)或以数字开头或结尾的字符串。 - Sinan Taifour
以前不是这样的。让我把它移除掉。 - Welbog
显示剩余3条评论

0

给你:

^(([a-zA-Z]([^a-zA-Z0-9]?[a-zA-Z0-9])*)|([0-9]([^a-zA-Z0-9]?[a-zA-Z0-9])*[a-zA-Z]+([^a-zA-Z0-9]?[a-zA-Z0-9])*))$

如果你想限制你想要接受的符号,只需将所有的 [^a-zA-Z0-9] 改为包含所有允许符号的 []。

是的...你可以将两种情况结合起来,但它仍然可以完成所需的任务。 - Rado

0
/^(?![\d_]+$)[A-Za-z0-9]+(?:_[A-Za-z0-9]+)*$/

你的问题本质上与这个相同,只是增加了一个要求,即至少有一个字符必须是字母。否定先行断言 - (?![\d_]+$) - 处理了这部分内容,并且比其他人试图将其合并到基本正则表达式中要容易得多(无论是阅读还是编写)。


-2
[A-Za-z][A-Za-z0-9_]*[A-Za-z]

对于你的前两个规则,这会起作用(因为第二个规则需要在开头和结尾都有一个字母,它自动要求字母)。

我不确定使用正则表达式是否可能实现第三个规则。


我认为你需要锚定你的正则表达式。 - Telemachus
不允许使用 te_t0123test - epochwolf
你完全错过了下划线。 - Sinan Taifour
哎呀,是的,忘记了下划线。还有那个单字符的东西。 - rpjohnst

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