【问题标题】:How do I match non-letters and non-numbers after a bunch of numbers?一堆数字后如何匹配非字母和非数字?
【发布时间】:2017-10-20 18:17:29
【问题描述】:

我使用的是 Ruby 2.4。我想匹配一堆非字母和数字,然后是一个或多个数字,然后是任意数量的非字母和数字。但是,这个字符串

2.4.0 :001 > token = "17 Milton,GA"
 => "17 Milton,GA"
...
2.4.0 :004 > Regexp.new("\\A([[:space:]]|[^\p{L}^0-9])*\\d+[^\p{L}^0-9]*\\z").match?(token.downcase)
 => true

与我的正则表达式匹配,我不希望它匹配,因为数字后面有字母。我需要在我的正则表达式中进行什么调整,以便在数字之后我唯一可以匹配的是非字母和非数字?

【问题讨论】:

  • 非字母,非数字后面一堆数字(?<=\d)[\W_]+
  • 你匹配[^\p{L}^0-9]是什么意思?除了字母和数字之外的任何字符?试试/\A[^[:alnum:]]*\d+[^[:alnum:]]*\z/。顺便说一句,我认为如果您在 \p => \\p 中添加反斜杠,您的正则表达式可能会起作用,因为您在 Regexp.new 构造函数中使用双引号字符串文字而不是正则表达式文字。

标签: ruby regex alphanumeric non-alphanumeric


【解决方案1】:

正则表达式存在一些问题。

1) 当您在 Regexp.new 构造函数中使用双引号字符串文字时,要声明文字反斜杠,您需要将其加倍 (\p => \\p)

2) [^\p{L}^0-9] 对于除字母和数字之外的任何字符都是错误的构造,因为第二个 ^ 被视为文字 ^ 符号。您至少需要删除第二个^。您也可以使用[^[:alnum:]] 匹配任何非字母数字符号。

3) 上面的模式也匹配空格,所以你不需要用[[:space]] 替换它。 ([[:space:]]|[^\p{L}^0-9])* -> [^\p{L}0-9]*.

所以,你可以使用固定的Regexp.new("\\A[^\\p{L}0-9]*\\d+[^\\p{L}0-9]*\\z") 正则表达式,或者使用

/\A[^[:alnum:]]*\d+[^[:alnum:]]*\z/.match?(token.downcase)

查看您的示例字符串与正则表达式不匹配的Rubular demo

详情

  • \A - 字符串开头
  • [^[:alnum:]]* - 0+ 个非字母数字字符
  • \d+ - 1 位以上
  • [^[:alnum:]]* - 0+ 个非字母数字字符
  • \z - 字符串结束。

【讨论】:

  • 你只是要让其他数字? rubular.com/r/lk9zOsGJ1M
  • 也许,这是 OP 的意图,因为原始模式包含 ASCII 数字范围。当然,使用 /\A[^\p{L}\p{N}]*\d+[^\p{L}\p{N}]*\z/ 将处理所有 Unicode 数字,但我不确定这是预期的。
  • 我不会这么快解释 Unicode 以及引擎如何实现它当然,你可以自己检查一下,(per unicode 9[[:alnum:]] (117,347 ) 和 [\p{L}\p{N}] (118, 258) 不匹配相同的项目。尽可能使用\w 可能会更好,但同样,[^\W_] 会在所有类似_ 的字符中留下。
【解决方案2】:

这里有三种方法可以做到这一点。

#1 对捕获组使用正则表达式

r = /
    \A                    # match beginning of string
    [^[[:alnum:]]]*       # match 0+ chars other than digits and lc letters
    (\d+)                 # match 1+ digits in capture group 1
    [^[[:alnum:]]]*       # match 0+ chars other than digits and lc letters
    \z                    # match end of string
    /x                    # free-spacing regex definition mode

"$ ^*123@-"[r, 1]         #=> '123'
"$ ^*123@-a?"[r, 1]       #=> nil
"$9^*123@-"[r, 1]         #=> nil

#2 使用带有\K 的正则表达式和积极的前瞻

r = /
    \A                    # match beginning of string
    [^[[:alnum:]]]*       # match 0+ chars other than digits and lc letters
    \K                    # discard all matched so far
    \d+                   # match 1+ digits
    (?=[^[[:alnum:]]]*\z) # match 0+ chars other than digits and lc letters
                          # in a positive lookahead
    /x                    # free-spacing mode

"$ ^*123@-"[r]            #=> '123'
"$ ^*123@-a?"[r]          #=> nil
"$9^*123@-"[r]            #=> nil

请注意,由于 Ruby 不支持可变长度的lookbehinds,因此我们不能使用正向lookbehind 代替\K

#3 将更简单的正则表达式与String 方法一起使用

def extract(str)
  return nil if str =~ /[[:alpha:]]/
  a = str.scan(/\d+/)
  a.size == 1 ? a.first : nil
end

extract("$ ^*123@-")      #=> '123'
extract("$ ^*123@-a?")    #=> nil
extract("$9^*123@-")      #=> nil

【讨论】:

  • 我有[^[[:alnum:]]]*,它使用POSIX 表达式[[:alnum:]],我以前有[^0-9a-z]*(和/ix)。我注意到@WiktorStribiżew 改用了[[:alnum:]]。这是一个更好的选择,所以我采用了它。它不仅更简单,而且可以识别非 ASCII(例如重音)字符。
猜你喜欢
  • 2014-02-19
  • 1970-01-01
  • 2014-03-05
  • 1970-01-01
  • 1970-01-01
  • 2015-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多