如何比较Ruby中的版本?


119

如何编写一段代码来比较某些版本的字符串并获得最新的代码?

例如字符串,如:'0.1', '0.2.1', '0.44'


我需要悲观的版本限制比较而回,但我不想依赖于RubyGems的做到这一点,所以我写了一个简单Version的类,它的一切,我需要:shorts.jeffkreeftmeijer.com/2014/...
jkreeftmeijer

Answers:


230
Gem::Version.new('0.4.1') > Gem::Version.new('0.10.1')

14
Gem::Version...语法让我以为我会需要安装一个宝石。但这不是必需的。
纪尧姆

注意:在Ruby 1.x上,这给我带来了有关未定义变量'Gem'的错误,但在Ruby 2.x上可以正常工作。在我的情况下,我正在检查RUBY_VERSION是否为Ruby 1.x(不是2.x),所以我只是像John Hyland和DigitalRoss一样进行了RUBY_VERSION.split('。')[0] ==“ 1”。
uliwitness 2013年

5
Gem::Dependency.new(nil, '~> 1.4.5').match?(nil, '1.4.6beta4')
levinalex 2013年

6
@uliwitness这不是Ruby 1.x与2.x;它是1.8.x和1.9+。Ruby从1.8.x开始默认不包含rubygems。您需要一个require 'rubygems'才能访问Gem名称空间。从1.9开始,它会自动包含在内。
马克·里德

这也用于比较通配符NPM版本。+1
深度元素


19
class Version < Array
  def initialize s
    super(s.split('.').map { |e| e.to_i })
  end
  def < x
    (self <=> x) < 0
  end
  def > x
    (self <=> x) > 0
  end
  def == x
    (self <=> x) == 0
  end
end
p [Version.new('1.2') < Version.new('1.2.1')]
p [Version.new('1.2') < Version.new('1.10.1')]

3
像这里的其他答案一样,您似乎在进行字符串比较而不是数字比较,这在比较'0.10'和'0.4'等版本时会引起问题。
约翰·海兰德

7
支持简洁的解决方案,不需要安装gem。
京东。

2
对于它的价值: vers = (1..3000000).map{|x| "0.0.#{x}"}; 'ok' puts Time.now; vers.map{|v| ComparableVersion.new(v) }.sort.first; puts Time.now # 24 seconds 2013-10-29 13:36:09 -0700 2013-10-29 13:36:33 -0700 => nil puts Time.now; vers.map{|v| Gem::Version.new(v) }.sort.first; puts Time.now # 41 seconds 2013-10-29 13:36:53 -0700 2013-10-29 13:37:34 -0700 代码blob使其丑陋,但基本上,使用此vs Gem :: Version的速度大约快一倍。
Shai

但是,版本不是数组。
塞尔吉奥·图伦采夫

15

您可以使用Versionomygem(可在github上找到):

require 'versionomy'

v1 = Versionomy.parse('0.1')
v2 = Versionomy.parse('0.2.1')
v3 = Versionomy.parse('0.44')

v1 < v2  # => true
v2 < v3  # => true

v1 > v2  # => false
v2 > v3  # => false

4
我已经看到了,但是要求我使用2颗宝石来做一件非常简单的事情。我想将其用作最后选择。
user239895

8
“不要重新发明轮子”。因为它很简单并不意味着程序员没有投入工作和思考。使用gem,阅读代码并从中学习-然后继续研究更大更好的产品!
跋涉

依赖性管理和版本维护是一个难题,可能比比较两个版本的任务困难得多。我完全同意,在这种情况下,引入另外2个依赖关系应该是最后的选择。
kkodev

10

我会做

a1 = v1.split('.').map{|s|s.to_i}
a2 = v2.split('.').map{|s|s.to_i}

那你可以做

a1 <=> a2

(以及其他所有“常规”比较)。

...如果您想进行<>测试,可以执行例如

(a1 <=> a2) < 0

或者如果您愿意,可以做一些其他的函数包装。


1
Array.class_eval {include Comparable}将使所有数组响应<,>等。或者,如果您只想对某些数组执行此操作:a = [1,2]; a.extend(Comparable)
韦恩·康拉德

4
我发现此解决方案的问题是它返回的是“ 1.2.0”大于“ 1.2”
Maria S

9

Gem::Version 是前往此处的简单方法:

%w<0.1 0.2.1 0.44>.map {|v| Gem::Version.new v}.max.to_s
=> "0.44"

比需要C扩展的Versionomy更好!
W. Andrew Loe III

我认为'max'不会起作用。它将报告0.5大于0.44。比较semver版本时,情况并非如此。
Flo Woo

2
这似乎已在最新的Gem :: Version中修复。现在正确报告0.44高于0.5。
Flo Woo

5

如果您希望不使用任何宝石而手工完成此任务,则如下所示的方法应该可以工作,尽管看起来有些麻烦。

versions = [ '0.10', '0.2.1', '0.4' ]
versions.map{ |v| (v.split '.').collect(&:to_i) }.max.join '.'

本质上,您将每个版本字符串都转换为整数数组,然后使用数组比较运算符。如果这是某些人需要维护的代码,则可以按步骤进行操作,以使操作更容易一些。


-1

我遇到了同样的问题,我想要一个没有Gem的版本比较器,并提出了以下内容:

def compare_versions(versionString1,versionString2)
    v1 = versionString1.split('.').collect(&:to_i)
    v2 = versionString2.split('.').collect(&:to_i)
    #pad with zeroes so they're the same length
    while v1.length < v2.length
        v1.push(0)
    end
    while v2.length < v1.length
        v2.push(0)
    end
    for pair in v1.zip(v2)
        diff = pair[0] - pair[1]
        return diff if diff != 0
    end
    return 0
end
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.