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

IT技术 javascript sorting
2021-02-07 19:32:59

这是软件版本号:

"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"

这更清楚地看到背后的想法......但是,如何将其转换为计算机程序??有没有人知道如何对此进行排序?谢谢你。

6个回答

进行这种比较的基本思想是使用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.mapand Array.every,这意味着它不会在 IE 9 之前的版本中运行。如果您需要支持那些,则必须为缺少的方法提供 polyfill。

这是带有一些单元测试的改进版本:jsfiddle.net/ripper234/Xv9WL/28
2021-03-13 19:32:59
例如,如果我们将“11.1.2”与“3.1.2”进行比较,您的算法将无法正常工作。在比较它们之前,您应该将字符串转换为整数。请解决这个问题;)
2021-03-16 19:32:59
@GabrielLittman:由经验丰富的开发人员编写的已经建立的库可以执行 semver 比较。
2021-03-20 19:32:59
@GabrielLittman:嘿,感谢您抽出时间来做这件事!但是,默认情况下,SO 上的所有代码都获得CC-BY-SA许可这意味着您不能让您的软件包获得 GPL 许可。我知道律师不是任何人在这里的目的,但如果你能解决它会很好。
2021-03-28 19:32:59
大家好,我已经将这个 gist 卷入了一个包含测试和所有内容的 gitrepo,并将它放在 npm 和 bower 上,这样我就可以更轻松地将它包含在我的项目中。 github.com/gabe0x02/version_compare
2021-03-29 19:32:59

服务器

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

$ npm 安装服务器

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

是的。是正确的答案 - 比较版本并非易事(请参阅semver.org 上的#11 ),并且有生产级库可以完成这项工作。
2021-03-11 19:32:59
@artuska 好吧,然后只需选择另一个包,例如semver-compare - 233B(小于 0.5kB!)gzipped :)
2021-03-11 19:32:59
NodeJS 不仅是服务器端唯一的解决方案。Electron 框架为桌面应用程序嵌入了一个 nodeJS。这实际上是我一直在寻找的答案。
2021-03-17 19:32:59
从技术上讲,这不是正确的答案,因为 node.js 和 javascript 是不同的。我认为最初的问题更多地针对浏览器。但是谷歌把我带到这里,幸运的是我正在使用节点:)
2021-03-31 19:32:59
semver 它是一个 npm 包,它可以在任何 JS 环境中使用!这是正确答案
2021-04-03 19:32:59
// 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"));
@Joe 我知道答案有点旧,但我正在使用该功能。测试a = '7'b = '7.0'返回,-1因为 7.0 更长。对此有什么建议吗?( console.log(compare("7", "7.0")); //returns -1)
2021-03-16 19:32:59
也许你在评论中推迟了我对英语的绊脚石......
2021-03-17 19:32:59
不。只看循环!如果一个字符串是另一个字符串的前缀(即循环到达结尾),则越长的字符串越高。
2021-03-22 19:32:59
我认为这一行:var len = Math.min(a_components.length, b_components.length);会导致版本 2.0.1.1 和 2.0.1 被视为平等吗?
2021-03-24 19:32:59
@RaphaelDDL 比较两个数组的长度,并在最短的数组中加 0,直到长度相等。
2021-03-24 19:32:59

这个非常小但非常快的比较函数采用任何长度任何数字大小的每个段的版本号

返回值:
-< 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"]

@emragins 你什么时候需要这样做?
2021-03-19 19:32:59
以“0.0”和“0.0.0”失败。见小提琴:jsfiddle.net/emragins/9e9pweqg
2021-03-20 19:32:59
我同意这是一个通常的观点。我将它与github.com/jonmiles/bootstrap-treeview一起使用,它以类似于版本的方式对节点进行分层,只是它实际上只是父/子节点及其索引。前任。父级:0.0,子级:0.0.0、0.0.1。有关我为什么关心的更多详细信息,请参阅此问题:github.com/jonmiles/bootstrap-treeview/issues/251
2021-03-30 19:32:59
请参阅此处的答案stackoverflow.com/questions/6611824/why-do-we-need-to-use-radix如果未指定,较旧的浏览器用于猜测基数参数。数字字符串中的前导零(例如“1.09.12”中的中间部分)曾经使用 radix=8 进行解析,结果是数字 0 而不是预期的数字 9。
2021-04-04 19:32:59
@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' 之前或反之亦然无关。
2021-04-07 19:32:59

最简单的是使用localeCompare

a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' })  

这将返回:

  • 0: 版本字符串相等
  • 1: 版本a大于b
  • -1: 版本b大于a
@Mordred 这就是我期望的行为,我不希望这些行为得到相同的对待。请参阅semver.org semver.org/#spec-item-11
2021-03-16 19:32:59
@JuanMendes 这确实有一个限制,即版本字符串必须始终具有相同数量的部分。所以当通过1.0and 时1.0.0.0localeCompare表明它1.0.0.0更大。
2021-03-23 19:32:59
@JuanMendes 简单的答案,我在发布问题 10 年后写了这篇文章:) 但这是个好主意,让我们开始投票吧!🎉
2021-03-30 19:32:59
为什么这没有更多的选票?有什么问题吗?它似乎通过了我写的所有测试。
2021-04-01 19:32:59
这是最简单的答案,爱它!
2021-04-10 19:32:59