如何使用js比较软件版本号?(仅数字)


164

这是软件版本号:

"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"

我该如何比较?假设正确的顺序是:

"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"

这个想法很简单...:先读第一个数字,然后读第二个,然后读第三个数字...。但是我无法将版本号转换为浮点数...。您也可以看到类似的版本号这个:

"1.0.0.0", "1.0.1.0", "2.0.0.0", "2.0.0.1", "2.0.1.0"

而且更清楚地看到背后的想法是什么...但是,如何将其转换为计算机程序?有人对如何排序有任何想法吗?谢谢。


5
这将是一个很好的嘶哑式面试问题。
史蒂夫·克拉里奇

2
这就是为什么所有软件版本号都应为2001403之类的整数的原因。当您要以友好的方式(如“ 2.0.14.3”)显示它时,请在演示时格式化版本号。
jarmod

2
此处的一般问题是语义版本比较,而且比较琐碎(请参阅semver.org上的#11 )。幸运的是,有一个官方的库,npm语义版本器
Dan Dascalescu 2015年

1
发现了一个简单的脚本来比较semvers
VSYNC

Answers:


132

进行比较的基本思想是使用Array.split从输入字符串中获取零件的数组,然后比较两个数组中的零件对。如果零件不相等,我们知道哪个版本较小。

有几个重要的细节要牢记:

  1. 每对中的部分应该如何比较?这个问题想进行数字比较,但是如果我们有不是仅由数字组成的版本字符串(例如“ 1.0a”)怎么办?
  2. 如果一个版本字符串比其他版本字符串包含更多部分,应该怎么办?应该最有可能将“ 1.0”视为小于“ 1.0.1”,但是“ 1.0.0”呢?

这是您可以直接使用的实现代码(带有文档的要点):

function versionCompare(v1, v2, options) {
    var lexicographical = options && options.lexicographical,
        zeroExtend = options && options.zeroExtend,
        v1parts = v1.split('.'),
        v2parts = v2.split('.');

    function isValidPart(x) {
        return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x);
    }

    if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
        return NaN;
    }

    if (zeroExtend) {
        while (v1parts.length < v2parts.length) v1parts.push("0");
        while (v2parts.length < v1parts.length) v2parts.push("0");
    }

    if (!lexicographical) {
        v1parts = v1parts.map(Number);
        v2parts = v2parts.map(Number);
    }

    for (var i = 0; i < v1parts.length; ++i) {
        if (v2parts.length == i) {
            return 1;
        }

        if (v1parts[i] == v2parts[i]) {
            continue;
        }
        else if (v1parts[i] > v2parts[i]) {
            return 1;
        }
        else {
            return -1;
        }
    }

    if (v1parts.length != v2parts.length) {
        return -1;
    }

    return 0;
}

该版本比较自然,不接受字符后缀,并认为“ 1.7”小于“ 1.7.0”。比较模式可以更改为词典编排,较短的版本字符串可以使用可选的第三个参数自动补零。

有一个说的jsfiddle运行“单元测试” 在这里 ; 它是ripper234的作品的略微扩展版本(谢谢)。

重要说明:此代码使用Array.mapArray.every,这意味着它将无法在早于9的IE版本中运行。如果需要支持这些代码,则必须为缺少的方法提供polyfills。


16
这是带有一些单元测试的改进版本:jsfiddle.net/ripper234/Xv9WL/28
ripper234

5
嗨,大家好,我已经将这个要点与测试和所有内容一起放到了gitrepo中,并放在npm和bower上,这样我可以更轻松地将其包含在我的项目中。 github.com/gabe0x02/version_compare
Gabriel Littman

2
@GabrielLittman:嘿,谢谢您抽出宝贵的时间!但是,默认情况下,SO上的所有代码均已通过CC-BY-SA许可。这意味着您不能让您的包裹获得GPL许可。我知道律师不是这里所有人的目标,但是如果您解决问题,那将是很好的。
2014年

2
@GabrielLittman:GPL实际上是非常严格的限制,因为您必须被迫GPL许可与现有GPL代码接触的所有代码。无论如何,以供将来参考:MIT是一种良好且广泛使用的“随心所欲,不附带任何条件”许可证。
2014年

3
@GabrielLittman:已经建立了由经验丰富的开发人员编写的执行semver比较的库。
Dan Dascalescu 2015年

82

塞姆弗

npm使用的语义版本解析器。

$ npm安装semver

var semver = require('semver');

semver.diff('3.4.5', '4.3.7') //'major'
semver.diff('3.4.5', '3.3.7') //'minor'
semver.gte('3.4.8', '3.4.7') //true
semver.ltr('3.4.8', '3.4.7') //false

semver.valid('1.2.3') // '1.2.3'
semver.valid('a.b.c') // null
semver.clean(' =v1.2.3 ') // '1.2.3'
semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true
semver.gt('1.2.3', '9.8.7') // false
semver.lt('1.2.3', '9.8.7') // true

var versions = [ '1.2.3', '3.4.5', '1.0.2' ]
var max = versions.sort(semver.rcompare)[0]
var min = versions.sort(semver.compare)[0]
var max = semver.maxSatisfying(versions, '*')

语义版本控制链接https :
//www.npmjs.com/package/semver#prerelease-identifiers


8
是。是正确的答案-比较版本是不平凡的(请参见semver.org的#11 ),并且有生产级的库可以完成这项工作。
Dan Dascalescu 2015年

7
从技术上讲,这不是正确的答案,因为node.js和javascript是不同的。我认为原来的问题更多地针对浏览器。但是谷歌把我带到了这里,幸运的是我正在使用node :)
Lee Gary

2
NodeJS不仅是服务器端的解决方案。Electron框架为桌面应用程序嵌入了一个nodeJS。这实际上是我一直在寻找的答案。
安东尼·雷蒙德

2
semver这是一个npm软件包,它可以在任何JS环境中使用!这是正确的答案
neiker

4
@artuska好吧,然后简单地去购买另一个软件包,例如semver-compare -233B(小于0.5kB!)gzip压缩
:)

50
// Return 1 if a > b
// Return -1 if a < b
// Return 0 if a == b
function compare(a, b) {
    if (a === b) {
       return 0;
    }

    var a_components = a.split(".");
    var b_components = b.split(".");

    var len = Math.min(a_components.length, b_components.length);

    // loop while the components are equal
    for (var i = 0; i < len; i++) {
        // A bigger than B
        if (parseInt(a_components[i]) > parseInt(b_components[i])) {
            return 1;
        }

        // B bigger than A
        if (parseInt(a_components[i]) < parseInt(b_components[i])) {
            return -1;
        }
    }

    // If one's a prefix of the other, the longer one is greater.
    if (a_components.length > b_components.length) {
        return 1;
    }

    if (a_components.length < b_components.length) {
        return -1;
    }

    // Otherwise they are the same.
    return 0;
}

console.log(compare("1", "2"));
console.log(compare("2", "1"));

console.log(compare("1.0", "1.0"));
console.log(compare("2.0", "1.0"));
console.log(compare("1.0", "2.0"));
console.log(compare("1.0.1", "1.0"));

我认为这条线:var len = Math.min(a_components.length, b_components.length);将导致版本2.0.1.1和2.0.1被视为等同吗?
乔恩·埃格顿

1
不。看一下循环!如果一个字符串是另一个的前缀(即循环到达末尾),则较长的字符串被视为较高。

也许您对我在评论中对英语的绊绊感到过……

我知道@Joe回答有点老,但是我正在使用该功能。测试a = '7'b = '7.0'返回,-1因为7.0更长。有什么建议吗?(console.log(compare("7", "7.0")); //returns -1
RaphaelDDL 2013年

我想这属于不确定行为的标题。如果您确实拥有这些版本号,那么我相信您可以修改逻辑以适合您的要求。
2013年

48

这个非常小但非常快速的比较功能,每段可以获取任何长度任何数字大小的版本号

返回值:
- < 0如果a <b
> 0则为数字- 如果a> b,则为数字
-0如果A = B

因此您可以将其用作 Array.sort()的比较函数;

编辑:错误修正的版本剥离尾随零以识别为相等的“ 1”和“ 1.0.0”

function cmpVersions (a, b) {
    var i, diff;
    var regExStrip0 = /(\.0+)+$/;
    var segmentsA = a.replace(regExStrip0, '').split('.');
    var segmentsB = b.replace(regExStrip0, '').split('.');
    var l = Math.min(segmentsA.length, segmentsB.length);

    for (i = 0; i < l; i++) {
        diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
        if (diff) {
            return diff;
        }
    }
    return segmentsA.length - segmentsB.length;
}

// TEST
console.log(
['2.5.10.4159',
 '1.0.0',
 '0.5',
 '0.4.1',
 '1',
 '1.1',
 '0.0.0',
 '2.5.0',
 '2',
 '0.0',
 '2.5.10',
 '10.5',
 '1.25.4',
 '1.2.15'].sort(cmpVersions));
// Result:
// ["0.0.0", "0.0", "0.4.1", "0.5", "1.0.0", "1", "1.1", "1.2.15", "1.25.4", "2", "2.5.0", "2.5.10", "2.5.10.4159", "10.5"]


失败,出现“ 0.0”和“ 0.0.0”。参见小提琴:jsfiddle.net/emragins/9e9pweqg
emragins

1
@emragins您何时需要执行此操作?
Skylar Ittner

1
@emragins:我看不到失败的地方。它输出["0.0.0", "0.0", "0.4.1", "0.5", "1.0.0", "1", "1.1", "1.2.15", "1.25.4", "2", "2.5.0", "2.5.10", "2.5.10.4159", "10.5"] 的代码输出["0.0", "0.0.0", "0.4.1", "0.5", "1", "1.0.0", "1.1", "1.2.15", "1.25.4", "2", "2.5.0", "2.5.10", "2.5.10.4159", "10.5"] 是完全相同的,因为0.0和0.0.0被认为是相等的,这意味着'0.0'在'0.0.0'之前是无关紧要的,反之亦然。
LeJared

我同意这是通常的观点。我将它与github.com/jonmiles/bootstrap-treeview一起使用,它以类似于版本的方式对节点进行分层,只是它实际上只是父/子节点及其索引。例如 父母:0.0,小孩:0.0.0,0.0.1。有关我为什么关注的更多详细信息,请参
见此

1
在此处查看答案stackoverflow.com/questions/6611824/why-do-we-ne-to-use-radix。如果未指定,则较旧的浏览器通常会猜测基数参数。甲在一个号码串像在“12年9月1日”的中间部分前导零用于与造成数0基数= 8,而不是预期数目9.被解析
LeJared

14

取自http://java.com/js/deployJava.js

    // return true if 'installed' (considered as a JRE version string) is
    // greater than or equal to 'required' (again, a JRE version string).
    compareVersions: function (installed, required) {

        var a = installed.split('.');
        var b = required.split('.');

        for (var i = 0; i < a.length; ++i) {
            a[i] = Number(a[i]);
        }
        for (var i = 0; i < b.length; ++i) {
            b[i] = Number(b[i]);
        }
        if (a.length == 2) {
            a[2] = 0;
        }

        if (a[0] > b[0]) return true;
        if (a[0] < b[0]) return false;

        if (a[1] > b[1]) return true;
        if (a[1] < b[1]) return false;

        if (a[2] > b[2]) return true;
        if (a[2] < b[2]) return false;

        return true;
    }

简单,但仅限于三个版本字段。
Dan Dascalescu

11

在这里找不到我想要的功能。所以我写了我自己的。这是我的贡献。我希望有人觉得它有用。

优点:

  • 处理任意长度的版本字符串。'1'或'1.1.1.1.1'。

  • 如果未指定,则将每个值默认为0。仅仅因为字符串更长并不意味着它是一个更大的版本。(“ 1”应与“ 1.0”和“ 1.0.0.0”相同。)

  • 比较数字而不是字符串。(“ 3” <“ 21”应为true。不能为false。)

  • 不要在循环中浪费时间进行无用的比较。(与==相比)

  • 您可以选择自己的比较器。

缺点:

  • 它不处理版本字符串中的字母。(我不知道那怎么办?)

我的代码,类似于乔恩接受的答案:

function compareVersions(v1, comparator, v2) {
    "use strict";
    var comparator = comparator == '=' ? '==' : comparator;
    if(['==','===','<','<=','>','>=','!=','!=='].indexOf(comparator) == -1) {
        throw new Error('Invalid comparator. ' + comparator);
    }
    var v1parts = v1.split('.'), v2parts = v2.split('.');
    var maxLen = Math.max(v1parts.length, v2parts.length);
    var part1, part2;
    var cmp = 0;
    for(var i = 0; i < maxLen && !cmp; i++) {
        part1 = parseInt(v1parts[i], 10) || 0;
        part2 = parseInt(v2parts[i], 10) || 0;
        if(part1 < part2)
            cmp = 1;
        if(part1 > part2)
            cmp = -1;
    }
    return eval('0' + comparator + cmp);
}

例子

compareVersions('1.2.0', '==', '1.2'); // true
compareVersions('00001', '==', '1.0.0'); // true
compareVersions('1.2.0', '<=', '1.2'); // true
compareVersions('2.2.0', '<=', '1.2'); // false

我认为此版本比批准的答案中的版本更好!
user3807877 2015年

1
如果比较器参数与未经检查的用户输入一起使用,则该功能易于注入代码!示例:compareVersions('1.2','== 0; alert(“ cotcha”);','1.2');
LeJared '16

@LeJared True。当我写它的时候,我们不会将它与用户提交的代码一起使用。应该把它作为一个缺点提出来。我现在更新了代码以消除这种可能性。现在,尽管如此,当webpack和其他node.js捆绑软件变得流行时,我建议使用semver在上面的Mohammed Akdim的答案几乎总是该问题的正确答案。
维克多

10

简单和简短的功能:

function isNewerVersion (oldVer, newVer) {
  const oldParts = oldVer.split('.')
  const newParts = newVer.split('.')
  for (var i = 0; i < newParts.length; i++) {
    const a = parseInt(newParts[i]) || 0
    const b = parseInt(oldParts[i]) || 0
    if (a > b) return true
    if (a < b) return false
  }
  return false
}

测试:

isNewerVersion('1.0', '2.0') // true
isNewerVersion('1.0', '1.0.1') // true
isNewerVersion('1.0.1', '1.0.10') // true
isNewerVersion('1.0.1', '1.0.1') // false
isNewerVersion('2.0', '1.0') // false
isNewerVersion('2', '1.0') // false
isNewerVersion('2.0.0.0.0.1', '2.1') // true
isNewerVersion('2.0.0.0.0.1', '2.0') // false

您可以使用以下方法简化它: const a = ~~ newParts [i]; 实际上,这是将字符串转换为整数的最有效方法,如果变量未定义或包含非数字字符,则返回0。
vanowm

5

如果这个想法已经在我未曾看到的链接中被访问过,请原谅我。

我已经成功地将零件转换成加权总和,如下所示:

partSum = this.major * Math.Pow(10,9);
partSum += this.minor * Math.Pow(10, 6);
partSum += this.revision * Math.Pow(10, 3);
partSum += this.build * Math.Pow(10, 0);

这使得比较非常容易(比较双精度)。我们的版本字段不得超过4位。

7.10.2.184  -> 7010002184.0
7.11.0.1385 -> 7011001385.0

我希望这对某人有帮助,因为多种条件似乎有些过分了。


2
如果this.minor> 999(将与专业重叠)
则将中断

5

这是另一个简短版本,适用于任意数量的子版本,填充零甚至带字母的偶数(1.0.0b3)

function compareVer(a, b)
{
    //treat non-numerical characters as lower version
    //replacing them with a negative number based on charcode of each character
    function fix(s)
    {
        return "." + (s.toLowerCase().charCodeAt(0) - 2147483647) + ".";
    }
    a = ("" + a).replace(/[^0-9\.]/g, fix).split('.');
    b = ("" + b).replace(/[^0-9\.]/g, fix).split('.');
    var c = Math.max(a.length, b.length);
    for (var i = 0; i < c; i++)
    {
        //convert to integer the most efficient way
        a[i] = ~~a[i];
        b[i] = ~~b[i];
        if (a[i] > b[i])
            return 1;
        else if (a[i] < b[i])
            return -1;
    }
    return 0;
}

输出:

0:a = b

1:a> b

-1:a <b

1.0.0.0.0.0 = 1.0
1.0         < 1.0.1
1.0b1       < 1.0
1.0a        < 1.0b
1.1         > 1.0.1b
1.1alpha    < 1.1beta
1.1rc1      > 1.1beta
1.0001      > 1.00000.1.0.0.0.01

https://jsfiddle.net/vanowm/p7uvtbor/


5

2017年答案:

v1 = '20.0.12'; 
v2 = '3.123.12';

compareVersions(v1,v2) 
// return positive: v1 > v2, zero:v1 == v2, negative: v1 < v2 
function compareVersions(v1, v2) {
        v1= v1.split('.')
        v2= v2.split('.')
        var len = Math.max(v1.length,v2.length)
        /*default is true*/
        for( let i=0; i < len; i++)
            v1 = Number(v1[i] || 0);
            v2 = Number(v2[i] || 0);
            if (v1 !== v2) return v1 - v2 ;
            i++;
        }
        return 0;
    }

现代浏览器的最简单代码:

 function compareVersion2(ver1, ver2) {
      ver1 = ver1.split('.').map( s => s.padStart(10) ).join('.');
      ver2 = ver2.split('.').map( s => s.padStart(10) ).join('.');
      return ver1 <= ver2;
 }

这里的想法是比较数字,但以字符串形式。为了使比较有效,两个字符串的长度必须相同。所以:

"123" > "99" 成为 "123" > "099"
填充所述短号码“修复”的比较

在这里,我用零填充每个部分的长度为10。然后使用简单的字符串比较作为答案

范例:

var ver1 = '0.2.10', ver2=`0.10.2`
//become 
ver1 = '0000000000.0000000002.0000000010'
ver2 = '0000000000.0000000010.0000000002'
// then it easy to see that
ver1 <= ver2 // true

你能解释一下函数compareVersion2到底会发生什么吗?
乌斯曼·瓦利

好,那么您可以使用substring而不是为了padStart获得更好的兼容性,即 var zeros = "0000000000"; '0.2.32'.split('.').map( s => zeros.substring(0, zeros.length-s.length) + s ).join('.') 会给您0000000000.0000000002.0000000032:)
Usman Wali


4

我的答案比这里的大多数答案都少

/**
 * Compare two semver versions. Returns true if version A is greater than
 * version B
 * @param {string} versionA
 * @param {string} versionB
 * @returns {boolean}
 */
export const semverGreaterThan = function(versionA, versionB){
  var versionsA = versionA.split(/\./g),
    versionsB = versionB.split(/\./g)
  while (versionsA.length || versionsB.length) {
    var a = Number(versionsA.shift()), b = Number(versionsB.shift())
    if (a == b)
      continue
    return (a > b || isNaN(b))
  }
  return false
}

1
您应该将其设为一个模块并将其放在node.js上。在那之前,我会窃取您的代码并归因于您。这次真是万分感谢。
r3wt '19

3

虽然这个问题已经有很多答案,但是每个人都在推广自己的后院解决方案,而与此同时,我们拥有经过(战斗)测试的库的整个生态系统。

NPMGitHub,X 上进行快速搜索将为我们提供一些可爱的库,而我想遍历一下:

semver-compare 是一个很棒的轻量级(〜230B)库,如果要按版本号排序,该库特别有用,因为库的暴露方法会返回 -101适当。

lib的核心:

module.exports = function cmp (a, b) {
    var pa = a.split('.');
    var pb = b.split('.');
    for (var i = 0; i < 3; i++) {
        var na = Number(pa[i]);
        var nb = Number(pb[i]);
        if (na > nb) return 1;
        if (nb > na) return -1;
        if (!isNaN(na) && isNaN(nb)) return 1;
        if (isNaN(na) && !isNaN(nb)) return -1;
    }
    return 0;
};

compare-semver 的大小相当大(压缩后约为4.4kB),但可以进行一些不错的独特比较,例如查找一堆版本的最小值/最大值,或者查找所提供的版本是否唯一或小于集合中的其他版本。版本。

compare-versions另一个小LIB(〜630B gzip压缩),并很好地遵循规范,这意味着你可以用α/β标志比较版本,甚至通配符(如轻微/补丁版本:1.0.x1.0.*

要点是:如果您可以通过选择的包管理器找到不错的(经过单元测试的)版本,则不一定总是需要从StackOverflow复制粘贴代码。


3

我遇到了类似的问题,并且已经为它创建了解决方案。随时尝试一下。

它返回0equal1如果版本greater-1如果是less

function compareVersion(currentVersion, minVersion) {
  let current = currentVersion.replace(/\./g," .").split(' ').map(x=>parseFloat(x,10))
  let min = minVersion.replace(/\./g," .").split(' ').map(x=>parseFloat(x,10))

  for(let i = 0; i < Math.max(current.length, min.length); i++) {
    if((current[i] || 0) < (min[i] || 0)) {
      return -1
    } else if ((current[i] || 0) > (min[i] || 0)) {
      return 1
    }
  }
  return 0
}


console.log(compareVersion("81.0.1212.121","80.4.1121.121"));
console.log(compareVersion("81.0.1212.121","80.4.9921.121"));
console.log(compareVersion("80.0.1212.121","80.4.9921.121"));
console.log(compareVersion("4.4.0","4.4.1"));
console.log(compareVersion("5.24","5.2"));
console.log(compareVersion("4.1","4.1.2"));
console.log(compareVersion("4.1.2","4.1"));
console.log(compareVersion("4.4.4.4","4.4.4.4.4"));
console.log(compareVersion("4.4.4.4.4.4","4.4.4.4.4"));
console.log(compareVersion("0","1"));
console.log(compareVersion("1","1"));
console.log(compareVersion("1","1.0.00000.0000"));
console.log(compareVersion("","1"));
console.log(compareVersion("10.0.1","10.1"));


2

这个想法是两个版本的比较,并且知道哪个是最大的版本。我们删除“。” 然后我们比较向量的每个位置

// Return 1  if a > b
// Return -1 if a < b
// Return 0  if a == b

function compareVersions(a_components, b_components) {

   if (a_components === b_components) {
       return 0;
   }

   var partsNumberA = a_components.split(".");
   var partsNumberB = b_components.split(".");

   for (var i = 0; i < partsNumberA.length; i++) {

      var valueA = parseInt(partsNumberA[i]);
      var valueB = parseInt(partsNumberB[i]);

      // A bigger than B
      if (valueA > valueB || isNaN(valueB)) {
         return 1;
      }

      // B bigger than A
      if (valueA < valueB) {
         return -1;
      }
   }
}

史诗般的答案,正是我想要的。
文斯

2
// Returns true if v1 is bigger than v2, and false if otherwise.
function isNewerThan(v1, v2) {
      v1=v1.split('.');
      v2=v2.split('.');
      for(var i = 0; i<Math.max(v1.length,v2.length); i++){
        if(v1[i] == undefined) return false; // If there is no digit, v2 is automatically bigger
        if(v2[i] == undefined) return true; // if there is no digit, v1 is automatically bigger
        if(v1[i] > v2[i]) return true;
        if(v1[i] < v2[i]) return false;
      }
      return false; // Returns false if they are equal
    }

1
欢迎来到SO。这个问题已经有很多好的答案,除非添加新的内容,否则请不要添加新的答案。
分机

1

replace()函数仅替换字符串中的第一个匹配项。因此,让更换.,。随后删除所有.,使,.再次解析它漂浮。

for(i=0; i<versions.length; i++) {
    v = versions[i].replace('.', ',');
    v = v.replace(/\./g, '');
    versions[i] = parseFloat(v.replace(',', '.'));
}

最后,对其进行排序:

versions.sort();

1

查看此博客文章。此功能适用于数字版本号。

function compVersions(strV1, strV2) {
  var nRes = 0
    , parts1 = strV1.split('.')
    , parts2 = strV2.split('.')
    , nLen = Math.max(parts1.length, parts2.length);

  for (var i = 0; i < nLen; i++) {
    var nP1 = (i < parts1.length) ? parseInt(parts1[i], 10) : 0
      , nP2 = (i < parts2.length) ? parseInt(parts2[i], 10) : 0;

    if (isNaN(nP1)) { nP1 = 0; }
    if (isNaN(nP2)) { nP2 = 0; }

    if (nP1 != nP2) {
      nRes = (nP1 > nP2) ? 1 : -1;
      break;
    }
  }

  return nRes;
};

compVersions('10', '10.0'); // 0
compVersions('10.1', '10.01.0'); // 0
compVersions('10.0.1', '10.0'); // 1
compVersions('10.0.1', '10.1'); // -1

1

例如,如果我们要检查当前jQuery版本是否小于1.8,则版本为“ 1.10.1”时parseFloat($.ui.version) < 1.8 )会给出错误的结果,因为parseFloat(“ 1.10.1”)返回1.1。字符串比较也会出错,因为"1.8" < "1.10"计算结果为false

所以我们需要这样的测试

if(versionCompare($.ui.version, "1.8") < 0){
    alert("please update jQuery");
}

以下函数可以正确处理此问题:

/** Compare two dotted version strings (like '10.2.3').
 * @returns {Integer} 0: v1 == v2, -1: v1 < v2, 1: v1 > v2
 */
function versionCompare(v1, v2) {
    var v1parts = ("" + v1).split("."),
        v2parts = ("" + v2).split("."),
        minLength = Math.min(v1parts.length, v2parts.length),
        p1, p2, i;
    // Compare tuple pair-by-pair. 
    for(i = 0; i < minLength; i++) {
        // Convert to integer if possible, because "8" > "10".
        p1 = parseInt(v1parts[i], 10);
        p2 = parseInt(v2parts[i], 10);
        if (isNaN(p1)){ p1 = v1parts[i]; } 
        if (isNaN(p2)){ p2 = v2parts[i]; } 
        if (p1 == p2) {
            continue;
        }else if (p1 > p2) {
            return 1;
        }else if (p1 < p2) {
            return -1;
        }
        // one operand is NaN
        return NaN;
    }
    // The longer tuple is always considered 'greater'
    if (v1parts.length === v2parts.length) {
        return 0;
    }
    return (v1parts.length < v2parts.length) ? -1 : 1;
}

这里有些例子:

// compare dotted version strings
console.assert(versionCompare("1.8",      "1.8.1")    <   0);
console.assert(versionCompare("1.8.3",    "1.8.1")    >   0);
console.assert(versionCompare("1.8",      "1.10")     <   0);
console.assert(versionCompare("1.10.1",   "1.10.1")   === 0);
// Longer is considered 'greater'
console.assert(versionCompare("1.10.1.0", "1.10.1")   >   0);
console.assert(versionCompare("1.10.1",   "1.10.1.0") <   0);
// Strings pairs are accepted
console.assert(versionCompare("1.x",      "1.x")      === 0);
// Mixed int/string pairs return NaN
console.assert(isNaN(versionCompare("1.8", "1.x")));
//works with plain numbers
console.assert(versionCompare("4", 3)   >   0);

请参阅此处以获取实时示例和测试套件:http : //jsfiddle.net/mar10/8KjvP/


arghh,刚刚注意到ripper234在几个月前的评论中张贴了一个小提琴网址,这非常相似。无论如何,我在这里保留我的答案...
2013

在这种情况下,此代码也将失败(与周围的大多数变体一样):versionCompare('1.09','1.1')返回“ 1”,与versionCompare('1.702','1.8')相同。
shaman.sir 2013年

该代码评估为“ 1.09”>“ 1.1”和“ 1.702”>“ 1.8”,我认为这是正确的。如果您不同意:您能否指出一些支持您观点的资源?
2013年

这取决于您的原则-据我所知,没有严格的规定。关于资源,维基百科在“递增序列”中有关“软件版本控制”的文章说1.81可能是1.8的次要版本,因此1.8应该读为1.80。语义版本控制文章semver.org/spec/v2.0.0.html也说1.9.0- > 1.10.0-> 1.11.0,因此将1.9.0这样比较起来就是1.90.0。因此,遵循此逻辑,版本1.702早于版本1.8(即1.800)。
shaman.sir

1
我看到一些规则对待1.8 <1.81 <1.9。但是在semver中,您将使用1.8.1而不是1.81。Semver(据我所知)是基于以下假设来定义的:递增零件将始终生成“更高版本”,因此1.8 <1.8.1 <1.9 <1.10 <1.81 <1.90 <1.100。我也看不到任何限制为两位数的迹象。所以我想说我的代码完全符合semver。
2013年

1

这是一个适用于Array.sort的coffeescript实现,灵感来自此处的其他答案:

# Returns > 0 if v1 > v2 and < 0 if v1 < v2 and 0 if v1 == v2
compareVersions = (v1, v2) ->
  v1Parts = v1.split('.')
  v2Parts = v2.split('.')
  minLength = Math.min(v1Parts.length, v2Parts.length)
  if minLength > 0
    for idx in [0..minLength - 1]
      diff = Number(v1Parts[idx]) - Number(v2Parts[idx])
      return diff unless diff is 0
  return v1Parts.length - v2Parts.length


结果['1.1.1','2.1.1,'3.3.1.0','3.1.1.0']
ertan2002 '19

1

我编写了一个用于对版本进行排序的节点模块,您可以在这里找到它:version-sort

特点

  • 无序列限制'1.0.1.5.53.54654.114.1.154.45'有效
  • 序列长度无限制:'1.1546515465451654654654654138754431574364364321353734'作品
  • 可以按版本对对象进行排序(请参阅自述文件)
  • 阶段(例如alpha,beta,rc1,rc2)

如果您需要其他功能,请不要犹豫。


1

这适用于以句点分隔的任何长度的数字版本。仅当myVersion> = minimumVersion时,它才返回true,并假设版本1小于1.0,版本1.1小于1.1.0,依此类推。添加额外的条件(例如接受数字(仅转换为字符串)和十六进制)或使定界符动态化(仅添加定界符参数,然后用参数替换“。”)应该相当简单。

function versionCompare(myVersion, minimumVersion) {

    var v1 = myVersion.split("."), v2 = minimumVersion.split("."), minLength;   

    minLength= Math.min(v1.length, v2.length);

    for(i=0; i<minLength; i++) {
        if(Number(v1[i]) > Number(v2[i])) {
            return true;
        }
        if(Number(v1[i]) < Number(v2[i])) {
            return false;
        }           
    }

    return (v1.length >= v2.length);
}

这是一些测试:

console.log(versionCompare("4.4.0","4.4.1"));
console.log(versionCompare("5.24","5.2"));
console.log(versionCompare("4.1","4.1.2"));
console.log(versionCompare("4.1.2","4.1"));
console.log(versionCompare("4.4.4.4","4.4.4.4.4"));
console.log(versionCompare("4.4.4.4.4.4","4.4.4.4.4"));
console.log(versionCompare("0","1"));
console.log(versionCompare("1","1"));
console.log(versionCompare("","1"));
console.log(versionCompare("10.0.1","10.1"));

另外,这里是一个递归版本

function versionCompare(myVersion, minimumVersion) {
  return recursiveCompare(myVersion.split("."),minimumVersion.split("."),Math.min(myVersion.length, minimumVersion.length),0);
}

function recursiveCompare(v1, v2,minLength, index) {
  if(Number(v1[index]) < Number(v2[index])) {
    return false;
  }
  if(Number(v1[i]) < Number(v2[i])) {
    return true;
    }
  if(index === minLength) {
    return (v1.length >= v2.length);
  }
  return recursiveCompare(v1,v2,minLength,index+1);
}

1

我找到一种比较它们的最简单方法,不确定是否是您想要的。当我在控制台中的代码下运行时,这很有意义,并且使用sort()方法,可以获得版本字符串的排序数组。它基于字母顺序。

"1.0" < "1.0.1" //true
var arr = ["1.0.1", "1.0", "3.2.0", "1.3"]
arr.sort();     //["1.0", "1.0.1", "1.3", "3.2.0"]

3
它不适用于两位版本号,例如1.10.0。
Leukipp

1

你可以使用String#localeCompareoptions

灵敏度

字符串中的哪些差异应导致非零结果值。可能的值为:

  • "base":只有基本字母不同的字符串才能比较为不相等。例如:a ≠ ba = áa = A
  • "accent":仅将基本字母或重音和其他音标不同的字符串比较为不相等。例如:a ≠ ba ≠ áa = A
  • "case":仅将基本字母或大小写不同的字符串比较为不相等。例如:a ≠ ba = áa ≠ A
  • "variant":基本字母,重音符号和其他变音符号或大小写不同的字符串比较不相等。还可以考虑其他差异。例如:a ≠ ba ≠ áa ≠ A

用法“ sort”的默认值为“ variant”;它取决于使用情况“搜索”的语言环境。

数字

是否应使用数字排序规则,例如“ 1” <“ 2” <“ 10”。可能的值为truefalse; 默认值为false。可以通过options属性或Unicode扩展键设置此选项。如果两者都提供,则该options属性优先。不需要实现来支持此属性。

var versions = ["2.0.1", "2.0", "1.0", "1.0.1", "2.0.0.1"];

versions.sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }));

console.log(versions);


这实际上如何运作?undefined以上是语言吗?当我阅读其他文章时,您如何设法发布此内容;)
mplungjan

undefined是语言环境的一部分,此处未使用。
Nina Scholz

0

您不能将它们转换成数字然后按大小排序吗?在长度小于4的数字后面加上0

在控制台中玩:

$(["1.0.0.0", "1.0.1.0", "2.0.0.0", "2.0.0.1", "2.0.1", "3.0"]).each(function(i,e) {
    var n =   e.replace(/\./g,"");
    while(n.length < 4) n+="0" ; 
    num.push(  +n  )
});

版本越大,数字越大。编辑:可能需要调整以考虑更大版本的系列


这只是一个例子,因为他必须做一些事情自己:P代替4,得到数最大的版本,然后填写那些低于量用0
康特拉

0

这是一个巧妙的把戏。如果要处理数字值,请在特定范围的值之间,为版本对象的每个级别分配一个值。例如,此处的“ largestValue”设置为0xFF,这会为您的版本创建非常“ IP”的外观。

这也可以处理字母数字版本控制(即1.2a <1.2b)

// The version compare function
function compareVersion(data0, data1, levels) {
    function getVersionHash(version) {
        var value = 0;
        version = version.split(".").map(function (a) {
            var n = parseInt(a);
            var letter = a.replace(n, "");
            if (letter) {
                return n + letter[0].charCodeAt() / 0xFF;
            } else {
                return n;
            }
        });
        for (var i = 0; i < version.length; ++i) {
            if (levels === i) break;
            value += version[i] / 0xFF * Math.pow(0xFF, levels - i + 1);
        }
        return value;
    };
    var v1 = getVersionHash(data0);
    var v2 = getVersionHash(data1);
    return v1 === v2 ? -1 : v1 > v2 ? 0 : 1;
};
// Returns 0 or 1, correlating to input A and input B
// Direct match returns -1
var version = compareVersion("1.254.253", "1.254.253a", 3);

0

我喜欢的版本@ mar10,不过从我的角度来看,存在误用的机会(看来,这是不是这样的,如果版本都兼容的语义版本的文件,但如果使用一些“集结号”可能是这种情况):

versionCompare( '1.09', '1.1');  // returns 1, which is wrong:  1.09 < 1.1
versionCompare('1.702', '1.8');  // returns 1, which is wrong: 1.702 < 1.8

这里的问题是,在某些情况下,版本号的子编号写有尾随零(至少就像我最近在使用其他软件时看到的那样),这类似于数字的有理部分,因此:

5.17.2054 > 5.17.2
5.17.2 == 5.17.20 == 5.17.200 == ... 
5.17.2054 > 5.17.20
5.17.2054 > 5.17.200
5.17.2054 > 5.17.2000
5.17.2054 > 5.17.20000
5.17.2054 < 5.17.20001
5.17.2054 < 5.17.3
5.17.2054 < 5.17.30

但是,第一个(或第一个和第二个)版本子编号始终被视为实际上等于的整数值。

如果使用这种版本控制,则可以在示例中仅更改几行:

// replace this:
p1 = parseInt(v1parts[i], 10);
p2 = parseInt(v2parts[i], 10);
// with this:
p1 = i/* > 0 */ ? parseFloat('0.' + v1parts[i], 10) : parseInt(v1parts[i], 10);
p2 = i/* > 0 */ ? parseFloat('0.' + v2parts[i], 10) : parseInt(v2parts[i], 10);

因此,除第一个数字外的每个子数字都将以浮点数进行比较,因此091它将变成0.090.1因此以这种方式正确进行比较。2054并且3会变得0.20540.3

完整的版本是(贷方为@ mar10):

/** Compare two dotted version strings (like '10.2.3').
 * @returns {Integer} 0: v1 == v2, -1: v1 < v2, 1: v1 > v2
 */
function versionCompare(v1, v2) {
    var v1parts = ("" + v1).split("."),
        v2parts = ("" + v2).split("."),
        minLength = Math.min(v1parts.length, v2parts.length),
        p1, p2, i;
    // Compare tuple pair-by-pair. 
    for(i = 0; i < minLength; i++) {
        // Convert to integer if possible, because "8" > "10".
        p1 = i/* > 0 */ ? parseFloat('0.' + v1parts[i], 10) : parseInt(v1parts[i], 10);;
        p2 = i/* > 0 */ ? parseFloat('0.' + v2parts[i], 10) : parseInt(v2parts[i], 10);
        if (isNaN(p1)){ p1 = v1parts[i]; } 
        if (isNaN(p2)){ p2 = v2parts[i]; } 
        if (p1 == p2) {
            continue;
        }else if (p1 > p2) {
            return 1;
        }else if (p1 < p2) {
            return -1;
        }
        // one operand is NaN
        return NaN;
    }
    // The longer tuple is always considered 'greater'
    if (v1parts.length === v2parts.length) {
        return 0;
    }
    return (v1parts.length < v2parts.length) ? -1 : 1;
}

PS速度较慢,但​​也可以考虑重新使用相同的比较函数,该操作实际上是字符串实际上是字符数组:

 function cmp_ver(arr1, arr2) {
     // fill the tail of the array with smaller length with zeroes, to make both array have the same length
     while (min_arr.length < max_arr.length) {
         min_arr[min_arr.lentgh] = '0';
     }
     // compare every element in arr1 with corresponding element from arr2, 
     // but pass them into the same function, so string '2054' will act as
     // ['2','0','5','4'] and string '19', in this case, will become ['1', '9', '0', '0']
     for (i: 0 -> max_length) {
         var res = cmp_ver(arr1[i], arr2[i]);
         if (res !== 0) return res;
     }
 }

0

我是根据Kons的想法制作的,并针对Java版本“ 1.7.0_45”进行了优化。它只是一个将版本字符串转换为浮点数的函数。这是功能:

function parseVersionFloat(versionString) {
    var versionArray = ("" + versionString)
            .replace("_", ".")
            .replace(/[^0-9.]/g, "")
            .split("."),
        sum = 0;
    for (var i = 0; i < versionArray.length; ++i) {
        sum += Number(versionArray[i]) / Math.pow(10, i * 3);
    }
    console.log(versionString + " -> " + sum);
    return sum;
}

字符串“ 1.7.0_45”被转换为1.0070000450000001,这足以进行常规比较。错误在这里解释:如何处理JavaScript中的浮点数精度?。如果任何部分需要3位以上的数字,则可以更改分频器Math.pow(10, i * 3);

输出将如下所示:

1.7.0_45         > 1.007000045
ver 1.7.build_45 > 1.007000045
1.234.567.890    > 1.23456789

0

我遇到了版本比较的相同问题,但是版本可能包含任何内容(即:不是点的分隔符,诸如rc1,rc2之类的扩展名...)。

我使用了它,基本上将版本字符串分为数字和非数字,然后尝试与类型进行比较。

function versionCompare(a,b) {
  av = a.match(/([0-9]+|[^0-9]+)/g)
  bv = b.match(/([0-9]+|[^0-9]+)/g)
  for (;;) {
    ia = av.shift();
    ib = bv.shift();
    if ( (typeof ia === 'undefined') && (typeof ib === 'undefined') ) { return 0; }
    if (typeof ia === 'undefined') { ia = '' }
    if (typeof ib === 'undefined') { ib = '' }

    ian = parseInt(ia);
    ibn = parseInt(ib);
    if ( isNaN(ian) || isNaN(ibn) ) {
      // non-numeric comparison
      if (ia < ib) { return -1;}
      if (ia > ib) { return 1;}
    } else {
      if (ian < ibn) { return -1;}
      if (ian > ibn) { return 1;}
    }
  }
}

在某些情况下,这里有一些假设,例如:“ 1.01” ===“ 1.1”,或“ 1.8” <“ 1.71”。如语义版本控制2.0.0所指定,它无法管理“ 1.0.0-rc.1” <“ 1.0.0”


0

在排序之前对版本进行预处理意味着不会不必要地多次调用parseInt。使用类似于Michael Deal的建议的Array#map,这是我用来查找标准3部分semver的最新版本的一种方法:

var semvers = ["0.1.0", "1.0.0", "1.1.0", "1.0.5"];

var versions = semvers.map(function(semver) {
    return semver.split(".").map(function(part) {
        return parseInt(part);
    });
});

versions.sort(function(a, b) {
    if (a[0] < b[0]) return 1;
    else if (a[0] > b[0]) return -1;
    else if (a[1] < b[1]) return 1;
    else if (a[1] > b[1]) return -1;
    else if (a[2] < b[2]) return 1;
    else if (a[2] > b[2]) return -1;
    return 0;
});

var newest = versions[0].join(".");
console.log(newest); // "1.1.0"

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.