首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在单字段上应用多个正则表达式

如何在单字段上应用多个正则表达式
EN

Stack Overflow用户
提问于 2016-12-07 17:34:36
回答 3查看 66关注 0票数 1

目前,我对美国的邮政编码有一个正则表达式:

代码语言:javascript
运行
复制
validates :zip,
          presence: true, 
          format: { with: /\A\d{5}(-\d{4})?\z/ }

我希望在相同邮政编码上的其他国家使用不同的正则表达式,因此正则表达式应该根据国家使用:

  • 澳大利亚4位数
  • 加拿大6位字母数字
  • 英国6-7位数字字母数字

有人能建议我怎样才能完全满足我的要求吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-12-07 18:10:34

您可能想要编写一个方法来帮助您:

代码语言:javascript
运行
复制
  validates :zip, presence: true, with: :zip_validator

  def zip_validator
    case country
    when 'AU'
      # some regex or fail
    when 'CA'
      # some other regex or fail
    when 'UK'
      # some other regex or fail
    else
      # should this fail?
    end
  end
票数 2
EN

Stack Overflow用户

发布于 2016-12-07 18:41:20

您可以给出一个lambda,它作为格式验证器(:with)的:with选项返回一个Regexp,这使它变得非常漂亮和干净:

代码语言:javascript
运行
复制
ZIP_COUNTRY_FORMATS = {
  'US' => /\A\d{5}(-\d{4})?\z/,
  'Australia' => /\A\d{4}\z/,
  # ...
}

validates :zip, presence: true,
  format: { with: ->(record){ ZIP_COUNTRY_FORMATS.fetch(record.country) } }

注意,它使用Hash#fetch而不是Hash#[],这样如果给出了一个不存在的country,它就会引发一个KeyError,就像一个理智检查一样。或者,您可以返回与任何内容匹配的默认Regexp:

代码语言:javascript
运行
复制
ZIP_COUNTRY_FORMATS.fetch(record.country, //)

...or nothing:

代码语言:javascript
运行
复制
ZIP_COUNTRY_FORMATS.fetch(record.country, /.\A/)

...depending关于你想要的行为。

票数 3
EN

Stack Overflow用户

发布于 2016-12-08 08:21:06

假设我们在散列中给出了每个国家的有效邮政编码示例,如下所示。

代码语言:javascript
运行
复制
example_pcs = {
  US:  ["",   "98230", "98230-1346"],
  CAN: ["*",  "V8V 3A2"],
  OZ:  ["!*", "NSW 1130", "ACT 0255", "VIC 3794", "QLD 4000", "SA 5664",
              "WA 6500", "TAS 7430", "NT 0874"]
}

其中每个数组的第一个元素是一串代码,稍后将对其进行解释。

我们可以根据这些信息为每个国家建立一个准则。(在一个实际的应用程序中,信息无疑是不同的,但我只是提出一个总体想法。)对于每个国家,我们为每个示例邮政编码构建一个regex,部分使用上述代码。然后,我们利用这些规则的联盟,为那个国家获得一个单一的规则。这里有一种方法可以构造一个示例邮政编码的regex。

代码语言:javascript
运行
复制
def make_regex(str, codes='')
  rstr = str.each_char.chunk do |c|
    case c
    when /\d/          then :DIGIT
    when /[[:alpha:]]/ then :ALPHA
    when /\s/          then :WHITE
    else                    :OTHER
    end
  end.
  map do |type, arr|
    case type
    when :ALPHA
      if codes.include?('!')
        arr
      elsif arr.size == 1
        "[[:alpha:]]"
      else "[[:alpha:]]\{#{arr.size}\}"
      end
    when :DIGIT
      (arr.size == 1) ? "\\d" : "\\d\{#{arr.size}\}"
    when :WHITE
      case codes
      when /\*/ then "\\s*"
      when /\+/ then "\\s+"
      else (arr.size == 1) ? "\\s" : "\\s\{#{arr.size}\}"
      end
    when :OTHER
      arr
    end
  end.
  join
  Regexp.new("\\A" << rstr << "\\z")
end

我让regex大小写对字母不敏感,但这当然可以改变。此外,对于一些国家,可能需要手动调整所产生的正则表达式,或者可能需要对邮政编码字符串进行预处理或后处理。例如,一些组合可能具有正确的格式,但仍然不是有效的邮政编码。例如,在澳大利亚,每个区域代码后面的四个数字必须在不同区域的指定范围内。

这里有一些例子。

代码语言:javascript
运行
复制
make_regex("12345")
  #=> /\A\d{5}\z/
make_regex("12345-1234")
  #=> /\A\d{5}-\d{4}\z/
Regexp.union(make_regex("12345"), make_regex("12345-1234"))
  #=> /(?-mix:\A\d{5}\z)|(?-mix:\A\d{5}-\d{4}\z)/

make_regex("V8V 3A2", "*")
  #=> /\A[[:alpha:]]\d[[:alpha:]]\s*\d[[:alpha:]]\d\z/ 

make_regex("NSW 1130", "!*")
  # => /\ANSW\s*\d{4}\z/

然后,对于每个国家,我们对每个示例邮政编码使用regexes的联合,将这些结果作为值保存在一个以国家代码为键的散列中。

代码语言:javascript
运行
复制
h = example_pcs.each_with_object({}) { |(country, (codes, *examples)), h|
  h[country] = Regexp.union(examples.map { |s| make_regex(s, codes) }.uniq) }
  #=> {:US=>/(?-mix:\A\d{5}\z)|(?-mix:\A\d{5}-\d{4}\z)/,
  #    :CAN=>/\A[[:alpha:]]\d[[:alpha:]]\s*\d[[:alpha:]]\d\z/,
  #    :OZ=>/(?-mix:\ANSW\s*\d{4}\z)|(?-mix:\AACT\s*\d{4}\z)|(?-mix:\AVIC\s*\d{4}\z)|(?-mix:\AQLD\s*\d{4}\z)|(?-mix:\ASA\s*\d{4}\z)|(?-mix:\AWA\s*\d{4}\z)|(?-mix:\ATAS\s*\d{4}\z)|(?-mix:\ANT\s*\d{4}\z)/} 

代码语言:javascript
运行
复制
"12345"      =~ h[:US]
  #=> 0
"12345-1234" =~ h[:US]
  #=> 0
"1234"       =~ h[:US]
  #=> nil
"12345 1234" =~ h[:US]
  #=> nil

"V8V 3A2"    =~ h[:CAN]
  #=> 0
"V8V    3A2" =~ h[:CAN]
  #=> 0
"V8v3a2"     =~ h[:CAN]
  #=> 0
"3A2 V8V"    =~ h[:CAN]
  #=> nil

"NSW 1132"   =~ h[:OZ]
   #=> 0
"NSW   1132" =~ h[:OZ]
   #=> 0
"NSW1132"    =~ h[:OZ]
   #=> 0
"NSW113"     =~ h[:OZ]
   #=> nil
"QLD"        =~ h[:OZ]
   #=> nil
"CAT 1132"   =~ h[:OZ]
   #=> nil

make_regex中执行的步骤

代码语言:javascript
运行
复制
str = "V8V 3A2"
codes = "*+"

如下。

代码语言:javascript
运行
复制
e = str.each_char.chunk do |c|
      case c
      when /\d/          then :DIGIT
      when /[[:alpha:]]/ then :ALPHA
      when /\s/          then :WHITE
      else                    :OTHER
      end
end
    #=> #<Enumerator: #<Enumerator::Generator:0x007f9ff201a330>:each>

通过将该枚举数转换为数组,我们可以看到它将生成的值。

代码语言:javascript
运行
复制
e.to_a
  #=> [[:ALPHA, ["V"]], [:DIGIT, ["8"]], [:ALPHA, ["V"]], [:WHITE, [" "]],
  #    [:DIGIT, ["3"]], [:ALPHA, ["A"]], [:DIGIT, ["2"]]] 

还在继续

代码语言:javascript
运行
复制
a = e.map do |type, arr|
    case type
    when :ALPHA
      if codes.include?('!')
        arr
      elsif arr.size == 1
        "[[:alpha:]]"
      else "[[:alpha:]]\{#{arr.size}\}"
      end
    when :DIGIT
      (arr.size == 1) ? "\\d" : "\\d\{#{arr.size}\}"
    when :WHITE
      case codes
      when /\*/ then "\\s*"
      when /\+/ then "\\s+"
      else (arr.size == 1) ? "\\s" : "\\s\{#{arr.size}\}"
      end
    when :OTHER
      arr
    end
  end
    #=> ["[[:alpha:]]", "\\d", "[[:alpha:]]", "\\s*", "\\d", "[[:alpha:]]", "\\d"]
rstr = a.join
  #=> "[[:alpha:]]\\d[[:alpha:]]\\s*\\d[[:alpha:]]\\d" 
t = "\\A" << rstr << "\\z"
  #=> "\\A[[:alpha:]]\\d[[:alpha:]]\\s*\\d[[:alpha:]]\\d\\z" 
puts t
  #=> \A[[:alpha:]]\d[[:alpha:]]\s*\d[[:alpha:]]\d\z
Regexp.new(t)
  #=> /\A[[:alpha:]]\d[[:alpha:]]\s*\d[[:alpha:]]\d\z/ 
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41023841

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档