Ruby 大家帮忙分析判断下从身份证中获取生日的写法的这段代码

wuwx · 2014年02月20日 · 最后由 swordray 回复于 2014年03月09日 · 3690 次阅读

假设 User 模型,有一个 id_number 记录用户的身份证号码,这个字段的值就有可能是 nil, "", 以及其他字符串,我刚开始的代码是这么写的:

if user.id_number.length == 18
    birthday = user.id_number[6, 8]
elsif user.id_number.length == 15
    birthday = "19" + user.id_number[6, 6]
end

代码华丽丽地异常了,原因是因为部分记录是 nil 没有 length 这个方法,于是开始动手改吧

if user.id_number.length && user.id_number.length == 18
    birthday = user.id_number[6, 8]
elsif user.id_number.length && user.id_number.length == 15
    birthday = "19" + user.id_number[6, 6]
end

这么写代码好像是可以工作了,不过有种便秘似的说不出的不悦……继续换个写法

if user.id_number.blank?
elsif user.id_number.length == 18
    birthday = user.id_number[6, 8]
elsif user.id_number.length == 15
    birthday = "19" + user.id_number[6, 6]
end

看上去少了一些,不过看着第一个空的 if,又有一种被灌翔了的感觉

if user.id_number.to_s.length == 18
    birthday = user.id_number[6, 8]
elsif user.id_number.to_s.length == 15
    birthday = "19" + user.id_number[6, 6]
end

这样看上去貌似靠谱一点点了有木有,不过感觉还是有很大的空间呢?

birthday = case user.id_number.to_s.length
    when 18 then user.id_number[6, 8]
    when 15 then "19" + user.id_number[6, 6]
    else "unknown"
end

好了,最终我的代码是像上面那么些的,大家帮忙看看靠谱不靠谱呢?或者有更好更 ruby 的写法木有的?

这个字段的值就有可能是 nil, "", 以及其他字符串

如果是其他字符串并且长度不是 18 或 15,是否需要给 birthday 一个默认值,比如"unknown"

我觉得你纠结的不在点上啊.... 如过id == nill 时这个birthday应该是什么状态?

身份证号码一般不是必填的吗?楼上能提出更好的 ruby 写法吗?我在等着呢

去除 if,可以用模式匹配:

lambda1 = lambda {}
lambda2 = lambda {}
cond = {
  :size1 => lambda1,
  :size2 => lambda2
}

cond[key] ? cone[key].call : xxxxx

最简单的正则,但是貌似实际应该比这复杂吧? /^\d{6}(\d{8})[0-9A-Z]{1,4}$/

#1 楼 @loveky 感谢建议,加上 else "unknown"了 #2 楼 @leozwa 按照一楼同学建议,变成 unknown 了 #3 楼 @zhangyanan 其实还有可能是护照号…… #4 楼 @Tim_Lang 相当于一个表,也是好办法 #5 楼 @sakura79 正则代码精炼,不过别人看起来不知道会不会有点难受……

#6 楼 @wuwx 我大概会这么写

def get_birthday_by_id(id)
  return unknown if id.nil?     # 我觉得到了这步了就应该再检查id格式是否正确了 检查nil足够
  (id.size == 18) ? id[6, 8] : "19" + user.id[6, 6]
end

很多人过生日还是过农历。 身份证上的生日很多录入时也是农历的。 不知道如何怎么转换成农历。

#7 楼 @leozwa 这样一个函数就有 2 个返回了……从结构上不是很好感觉 #8 楼 @zealinux 其实就是从身份证里抽出阳历生日信息啦

#9 楼 @wuwx 风格个问题吧 我比较喜欢 early return 不过因为只有两行所以看起来确实有些不妥

@leozwa 你第二句挺好的,第一句检查if id.nil?我觉得多余。这种情况不用检查,没有 id 应该直接报错"Invalid Argument"。

不过还是应该检查一下 id 是否合格。借用你的代码

def get_birthday_by_id(id)
  case id.size
  when 18
    id[6, 8]
  when 19
    id[6, 6]
  else
    raise "invalid id"
  end
end

#11 楼 @billy get_birthday_by_id(nil)的话应该是不报错的 并不是没有 id 而是 id 为 nil

@leozwa, 不对的。如果没有 id,也没有检查 nil,那么会报错 no method "size" for nil。这是期望的结果,不用检查。

第三种最清晰, to_s.length 这种用法太 trick 了。

身份证号码你都有了。你完全可以连上公安的系统再查一次啊,所有信息都能查个底朝天了!:)

我想说为什么这个字段会有可能是除了 18 位或者 16 位数字以外的字段?难道录入的时候没有验证过吗?

#16 楼 @modacker 有可能是护照,军官证等……

用 try 呗 user.id_number.try(:length)

#19 楼 @towonzhou undefined method `try' for nil:NilClass

第二种就很清晰,强迫症要不得.....

一句话能说清楚的事情,就不要写两行以上代码了


[/^\d{6}(\d{8})\d{3}.$/, /^\d{6}(\d{6})\d{3}$/].map { |r| s.to_s.match(r) && ('19' + s.to_s.match(r).to_a[1])[-8..-1] }.find(&:present?)

#22 楼 @swordray 我个人觉得我宁愿用两行以上的代码,这个可读性也太低了。

#23 楼 @shiny  这里用的 Ruby / Rails 方法都是很常用的,看多了就习惯了

#24 楼 @swordray 不知道你有没有感觉到代码的坏味道呢? 而且你数数字符数,几乎没比之前的写法少。😄

需要 登录 后方可回复, 如果你还没有账号请 注册新账号