以下是软件版本号:

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

这样可以更清楚地看到背后的想法。 但是,我怎样才能把它转换成计算机程序呢?


当前回答

下面是另一个简短的版本,适用于任何数量的子版本,填充零和偶数字母(1.0.0b3)

const compareVer = ((prep, repl) =>
{
  prep = t => ("" + t)
      //treat non-numerical characters as lower version
      //replacing them with a negative number based on charcode of first character
    .replace(/[^0-9\.]+/g, c => "." + (c.replace(/[\W_]+/, "").toLowerCase().charCodeAt(0) - 65536) + ".")
      //remove trailing "." and "0" if followed by non-numerical characters (1.0.0b);
    .replace(/(?:\.0+)*(\.-[0-9]+)(\.[0-9]+)?\.*$/g, "$1$2")
    .split('.');

  return (a, b, c, i, r) =>
  {
    a = prep(a);
    b = prep(b);
    for (i = 0, r = 0, c = Math.max(a.length, b.length); !r && i++ < c;)
    {
      r = -1 * ((a[i] = ~~a[i]) < (b[i] = ~~b[i])) + (a[i] > b[i]);
    }
    return r;
  }
})();

函数返回:

如果a = b则为0

1如果a > b

-1如果a < b

1.0         = 1.0.0.0.0.0
1.0         < 1.0.1
1.0b1       < 1.0
1.0b        = 1.0b
1.1         > 1.0.1b
1.1alpha    < 1.1beta
1.1rc1      > 1.1beta
1.1rc1      < 1.1rc2
1.1.0a1     < 1.1a2
1.1.0a10    > 1.1.0a1
1.1.0alpha  = 1.1a
1.1.0alpha2 < 1.1b1
1.0001      > 1.00000.1.0.0.0.01

/*use strict*/ const compareVer = ((prep, repl) => { prep = t => ("" + t) //treat non-numerical characters as lower version //replacing them with a negative number based on charcode of first character .replace(/[^0-9\.]+/g, c => "." + (c.replace(/[\W_]+/, "").toLowerCase().charCodeAt(0) - 65536) + ".") //remove trailing "." and "0" if followed by non-numerical characters (1.0.0b); .replace(/(?:\.0+)*(\.-[0-9]+)(\.[0-9]+)?\.*$/g, "$1$2") .split('.'); return (a, b, c, i, r) => { a = prep(a); b = prep(b); for (i = 0, r = 0, c = Math.max(a.length, b.length); !r && i++ < c;) { r = -1 * ((a[i] = ~~a[i]) < (b[i] = ~~b[i])) + (a[i] > b[i]); } return r; } })(); //examples let list = [ ["1.0", "1.0.0.0.0.0"], ["1.0", "1.0.1"], ["1.0b1", "1.0"], ["1.0b", "1.0b"], ["1.1", "1.0.1b"], ["1.1alpha", "1.1beta"], ["1.1rc1", "1.1beta"], ["1.1rc1", "1.1rc2"], ["1.1.0a1", "1.1a2"], ["1.1.0a10", "1.1.0a1"], ["1.1.0alpha", "1.1a"], ["1.1.0alpha2", "1.1b1"], ["1.0001", "1.00000.1.0.0.0.01"] ] for(let i = 0; i < list.length; i++) { console.log( list[i][0] + " " + "<=>"[compareVer(list[i][0], list[i][1]) + 1] + " " + list[i][1] ); }

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

其他回答

// 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
    }

下面是一个版本,它对版本字符串进行排序,而不分配任何子字符串或数组。由于它分配的对象更少,GC要做的工作也就更少。

有一对分配(允许重用getVersionPart方法),但是如果您对性能非常敏感,您可以扩展它以完全避免分配。

const compareVersionStrings : (a: string, b: string) => number = (a, b) =>
{
    var ia = {s:a,i:0}, ib = {s:b,i:0};
    while (true)
    {
        var na = getVersionPart(ia), nb = getVersionPart(ib);

        if (na === null && nb === null)
            return 0;
        if (na === null)
            return -1;
        if (nb === null)
            return 1;
        if (na > nb)
            return 1;
        if (na < nb)
            return -1;
    }
};

const zeroCharCode = '0'.charCodeAt(0);

const getVersionPart = (a : {s:string, i:number}) =>
{
    if (a.i >= a.s.length)
        return null;

    var n = 0;
    while (a.i < a.s.length)
    {
        if (a.s[a.i] === '.')
        {
            a.i++;
            break;
        }

        n *= 10;
        n += a.s.charCodeAt(a.i) - zeroCharCode;
        a.i++;
    }
    return n;
}

在排序之前对版本进行预处理意味着parseInt不会被不必要地多次调用。 使用array# map类似于Michael Deal的建议,下面是我用来查找标准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"

进行这种比较的基本思想是使用Array。拆分以从输入字符串中获得部件数组,然后比较两个数组中的部件对;如果部分不相等,我们就知道哪个版本更小。

这里有一些重要的细节需要记住:

每对零件应该如何比较?这个问题想要从数字上进行比较,但是如果我们有不只是由数字组成的版本字符串(例如。“1.0”)? 如果一个版本字符串的部分比另一个多,会发生什么?很可能“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。map和Array。这意味着它将不会在9之前的IE版本中运行。如果你需要支持这些方法,你就必须为缺失的方法提供填充。

我认为这是一个值得分享的实现,因为它简短,简单,但功能强大。请注意,它只使用数字比较。通常它会检查version2是否比version1晚,如果是,则返回true。假设您有version1: 1.1.1和version2: 1.1.2。它遍历两个版本的每个部分,将它们的部分相加如下:对于版本1(1 + 0.1)然后(1.1 + 0.01),对于版本2(1 + 0.1)然后(1.1 + 0.02)。

function compareVersions(version1, version2) {

    version1 = version1.split('.');
    version2 = version2.split('.');

    var maxSubVersionLength = String(Math.max.apply(undefined, version1.concat(version2))).length;

    var reduce = function(prev, current, index) {

        return parseFloat(prev) + parseFloat('0.' + Array(index + (maxSubVersionLength - String(current).length)).join('0') + current);
    };

    return version1.reduce(reduce) < version2.reduce(reduce);
}

如果你想从版本列表中找到最新的版本,那么这可能是有用的:

function findLatestVersion(versions) {

    if (!(versions instanceof Array)) {
        versions = Array.prototype.slice.apply(arguments, [0]);
    }

    versions = versions.map(function(version) { return version.split('.'); });

    var maxSubVersionLength = String(Math.max.apply(undefined, Array.prototype.concat.apply([], versions))).length;

    var reduce = function(prev, current, index) {

        return parseFloat(prev) + parseFloat('0.' + Array(index + (maxSubVersionLength - String(current).length)).join('0') + current);
    };

    var sums = [];

    for (var i = 0; i < versions.length; i++) {
        sums.push(parseFloat(versions[i].reduce(reduce)));
    }

    return versions[sums.indexOf(Math.max.apply(undefined, sums))].join('.');
}

console.log(findLatestVersion('0.1000000.1', '2.0.0.10', '1.6.10', '1.4.3', '2', '2.0.0.1')); // 2.0.0.10
console.log(findLatestVersion(['0.1000000.1', '2.0.0.10', '1.6.10', '1.4.3', '2', '2.0.0.1'])); // 2.0.0.10