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

Posted

技术标签:

【中文标题】如何使用js比较软件版本号? (只有数字)【英文标题】:How to compare software version number using js? (only number) 【发布时间】:2011-10-13 12:57:27 【问题描述】:

这里是软件版本号:

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

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

【问题讨论】:

这将是一个很好的 fizzbuzz 类型的面试问题。 这就是为什么所有软件版本号都应该是整数,比如 2001403。当你想以某种友好的方式显示它,比如“2.0.14.3”时,你可以在演示时格式化版本号。 这里的一般问题是语义版本比较,这很重要(参见semver.org 的#11)。幸运的是,有一个官方库,semantic versioner for npm。 找到一个比较semvers的simple script @jarmod 所以你有2001403,是2.0.14.3 还是20.1.4.3 还是2.0.1.43?这种方法即使没有缺陷也是有限的。 【参考方案1】:

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

有一些重要的细节需要牢记:

    应该如何比较每对中的零件?问题想要进行数字比较,但如果我们的版本字符串不是仅由数字组成(例如“1.0a”)怎么办? 如果一个版本字符串的部分比另一个多,会发生什么情况?很可能“1.0”应该被认为小于“1.0.1”,但是“1.0.0”呢?

这是您可以直接使用的实现代码 (gist with documentation):

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;

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

有一个运行“单元测试”here 的 JSFiddle;它是ripper234's work 的略微扩展版本(谢谢)。

重要提示:此代码使用Array.mapArray.every,这意味着它不会在IE 9之前的版本中运行。如果您需要支持这些,则必须提供polyfills对于缺少的方法。

【讨论】:

这是一个带有一些单元测试的改进版本:jsfiddle.net/ripper234/Xv9WL/28 例如,如果我们将“11.1.2”与“3.1.2”进行比较,您的算法就不能正常工作。您应该在比较它们之前将字符串转换为整数。请解决这个问题;) 大家好,我已经将这个要点整合到一个带有测试和所有内容的 gitrepo 中,并将它放在 npm 和 bower 上,这样我就可以更轻松地将它包含在我的项目中。 github.com/gabe0x02/version_compare @GabrielLittman:嘿,感谢您抽出宝贵的时间!但是,默认情况下,SO 上的所有代码都使用CC-BY-SA 许可。这意味着你不能让你的包获得 GPL 许可。我知道律师不是任何人都在这里的,但如果你能解决它会很好。 @GabrielLittman:已经有 established libraries written by seasoned devs 执行 semver 比较。【参考方案2】:

semver

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

$ npm install 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

【讨论】:

是的。 这是是正确的答案 - 比较版本很重要(参见semver.org 的#11),并且有生产级库可以完成这项工作。 从技术上讲,这不是正确的答案,因为 node.js 和 javascript 是不同的。我认为最初的问题更多地针对浏览器。但是谷歌把我带到了这里,幸运的是我正在使用节点:) NodeJS 不仅仅是服务器端的解决方案。 Electron 框架为桌面应用程序嵌入了一个 nodeJS。这实际上是我一直在寻找的答案。 semver 它是一个 npm 包,可以在任何 JS 环境中使用!这是正确的答案 @artuska 好吧,然后简单地选择另一个包,例如 semver-compare - 233B(小于 0.5kB!)gzip :)【参考方案3】:
// 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 被视为相同,对吗? 没有。看看循环之后!如果一个字符串是另一个字符串的前缀(即循环到达末尾),则较长的字符串被视为较高。 也许你被我在评论中的英语绊倒推迟了...... @Joe 我知道答案有点旧,但我正在使用该功能。测试a = '7'b = '7.0' 返回-1,因为7.0 更长。对此有什么建议吗? (console.log(compare("7", "7.0")); //returns -1) @RaphaelDDL 比较两个数组的长度,并在最短的长度上加 0,直到长度相等。【参考方案4】:

这个非常小,但速度非常快的比较函数接受任何长度的版本号每个段的任何数字大小

返回值: - 一个数字 &lt; 0 如果 a - 一个数字 &gt; 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 您什么时候需要这样做? @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',反之亦然。 我同意这是一个常见的观点。我将它与github.com/jonmiles/bootstrap-treeview 一起使用,它以类似于版本的方式对节点进行分层,只是它实际上只是父/子节点及其索引。前任。父级:0.0,子级:0.0.0、0.0.1。有关我为什么关心的更多详细信息,请参阅此问题:github.com/jonmiles/bootstrap-treeview/issues/251 在此处查看答案***.com/questions/6611824/why-do-we-need-to-use-radix。如果未指定,旧版浏览器会猜测 radix 参数。数字字符串中的前导零(如“1.09.12”中的中间部分)过去使用 radix=8 解析,导致数字 0 而不是预期的数字 9。【参考方案5】:

最简单的就是使用localeCompare

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

这将返回:

0:版本字符串相等 1:版本a大于b -1:版本b大于a

【讨论】:

这是最简单的答案,爱它! 为什么这没有更多的选票?有什么问题吗?它似乎通过了我编写的所有测试。 @JuanMendes 简单的答案,我在发布问题 10 年后写了这篇文章 :) 但这是个好主意,让我们开始投票吧! ? @JuanMendes 这确实有一个限制,即版本字符串必须始终具有相同数量的部分。所以当通过1.01.0.0.0时,localeCompare表明1.0.0.0更大。 @Mordred 这就是我所期望的行为,我不希望这些行为会受到同样的对待。见semver.orgsemver.org/#spec-item-11【参考方案6】:

简单而简短的功能:

function isNewerVersion (oldVer, newVer) 
  const oldParts = oldVer.split('.')
  const newParts = newVer.split('.')
  for (var i = 0; i < newParts.length; i++) 
    const a = ~~newParts[i] // parse int
    const b = ~~oldParts[i] // parse int
    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]; 实际上这是将字符串转换为整数的最有效方法,如果变量未定义或包含非数字字符。 我经常需要知道它是新的还是相同的,所以我的代码可以决定是否隐藏不支持的功能。这不是大多数人感兴趣的问题吗? 漂亮又简短,正是我想要的。您还可以添加 oldVer.replace(/[^0-9.]/g, '').trim()newVer.replace(/[^0-9.]/g, '').trim() 来处理添加文本的 alpha、beta 或发布候选版本,如下所示:`1.0.0-rc'【参考方案7】:

取自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;
    

【讨论】:

简单,但仅限于三个版本字段。 意识到我来晚了,但我真的很喜欢这种简单的语义版本控制解决方案,因为您将拥有三个版本字段。 终于有了一个我可以轻松阅读的版本。是的,三个版本字段是标准,所以这对我们大多数人都很有用【参考方案8】:

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

优点:

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

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

比较数字而不是字符串。 ('3'

不要在循环中的无用比较上浪费时间。 (比较 ==)

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

缺点:

它不处理版本字符串中的字母。 (我什至不知道这会如何工作?)

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

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

【讨论】:

我认为这个版本比批准答案中的版本好! 如果比较器参数与未经检查的用户输入一起使用,此函数很容易发生代码注入!示例: compareVersions('1.2', '==0;alert("cotcha");', '1.2'); @LeJared 真的。但是,当我编写它时,我们不会将它与用户提交的代码一起使用。应该把它作为一个骗局提出来。我现在更新了代码以消除这种可能性。不过现在,当 webpack 和其他 node.js 捆绑器变得流行时,我建议 Mohammed Akdim 上面使用 semver 的答案几乎总是这个问题的正确答案。【参考方案9】:

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

const compareVer = ((prep, repl) =>

  repl = c => "." + ((c = c.replace(/[\W_]+/, "")) ? c.toLowerCase().charCodeAt(0) - 65536 : "") + ".";
  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, repl)
      //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); i < c && !r; i++)
    
      r = -1 * ((a[i] = ~~a[i]) < (b[i] = ~~b[i])) + (a[i] > b[i]);
    
    return r;
  
)();

函数返回:

0 : a = b

1 : a &gt; b

-1 : a &lt; 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) =>

  repl = c => "." + ((c = c.replace(/[\W_]+/, "")) ? c.toLowerCase().charCodeAt(0) - 65536 : "") + ".";
  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, repl)
      //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); i < c && !r; i++)
    
      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/

【讨论】:

【参考方案10】:

虽然这个问题已经有很多个答案,但每个人都在推广他们自己的后院酿造的解决方案,而我们有一个完整的生态系统,由经过(战斗)测试的库组成。

在NPM、GitHub 上快速搜索,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 是另一个小型库(约 630B gzipped)并且很好地遵循规范,这意味着您可以比较带有 alpha/beta 标志甚至通配符的版本(例如次要/补丁版本:1.0.x1.0.*

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

【讨论】:

【参考方案11】:

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" &gt; "99"变成"123" &gt; "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 =&gt; zeros.substring(0, zeros.length-s.length) + s ).join('.') 将为您提供0000000000.0000000002.0000000032 :)【参考方案12】:

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

对于equal,它返回0,如果版本是greater,则返回1,如果是-1,则返回-1

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

【讨论】:

不需要正则表达式。您可以简单地将. 附加到map() 中:x=&gt;parseFloat("." + x, 10)【参考方案13】:

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

我在将部分转换为加权和方面取得了一些成功,如下所示:

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

我希望这对某人有所帮助,因为多个条件似乎有点矫枉过正。

【讨论】:

如果this.minor > 999(将与major重叠),则会中断【参考方案14】:

检查函数version_compare() from the php.js project。类似于PHP's version_compare()

你可以像这样简单地使用它:

version_compare('2.0', '2.0.0.1', '<'); 
// returns true

【讨论】:

【参考方案15】:

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

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

【讨论】:

你应该把它做成一个模块并将它放在 node.js 上。在那之前,我正在窃取你的代码并归因于你。谢谢你。【参考方案16】:

2020 年(大部分时间)正确的 JavaScript 答案

2020 年 3 月的 Nina Scholz 和 2020 年 4 月的 Sid Vishnoi 都发布了现代答案:

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);

localCompare 已经存在了一段时间

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator

但是 1.0a1.0.1 呢?

localCompare 没有解决这个问题,仍然返回 1.0.1 , 1.0a

Michael Deal 在他的(冗长而复杂的)解决方案already cracked that in 2013

他将Numbers转换为另一个Base,这样它们可以更好地排序

他的回答让我思考......

666 - 不要考虑数字 - 999

排序是字母数字的,基于 ASCII 值,所以让我们(ab)使用 ASCII 作为“基础”

我的解决方案是将 1.0.2.1 转换为 b.a.c.bbacb ,然后排序

这解决了 1.1 vs. 1.0.0.0.1bb vs. baaab

并立即解决了 1.0a1.0.1 的排序问题:baabab

转换完成:

    const str = s => s.match(/(\d+)|[a-z]/g)
                      .map(c => c == ~~c ? String.fromCharCode(97 + c) : c);

= 计算 0...999 数字的 ASCII 值,否则连接字母

1.0a >>> [ "1" , "0" , "a" ] >>> [ "b" , "a" , "a" ]

为了比较,没有必要用.join("")将它连接到一个字符串

Oneliner

const sortVersions=(x,v=s=>s.match(/(\d+)|[a-z]/g)
                            .map(c=>c==~~c?String.fromCharCode(97+c):c))
                    =>x.sort((a,b)=>v(b)<v(a)?1:-1)

测试sn-p:

function log(label,val)
  document.body.append(label,String(val).replace(/,/g," - "),document.createElement("BR"));


let v = ["1.90.1", "1.9.1", "1.89", "1.090", "1.2", "1.0a", "1.0.1", "1.10", "1.0.0a"];
log('not sorted input :',v);

v.sort((a, b) => a.localeCompare(b,undefined,numeric:true,sensitivity:'base'   ));
log(' locale Compare :', v); // 1.0a AFTER 1.0.1

const str = s => s.match(/(\d+)|[a-z]/g)
                  .map(c => c == ~~c ? String.fromCharCode(97 + c) : c);
const versionCompare = (a, b) => 
  a = str(a);
  b = str(b);
  return b < a ? 1 : a == b ? 0 : -1;


v.sort(versionCompare);
log('versionCompare:', v);

注意 1.090 在两个结果中的排序方式。

我的代码不会解决一个答案中提到的 001.012.001 表示法,但 localeCompare 正确地解决了这部分挑战。

你可以结合这两种方法:

当涉及到一封信时,使用.localCompareversionCompare 进行排序

最终的 JavaScript 解决方案

const sortVersions = (
  x,
  v = s => s.match(/[a-z]|\d+/g).map(c => c==~~c ? String.fromCharCode(97 + c) : c)
) => x.sort((a, b) => (a + b).match(/[a-z]/) 
                             ? v(b) < v(a) ? 1 : -1 
                             : a.localeCompare(b, 0, numeric: true))

let v=["1.90.1","1.090","1.0a","1.0.1","1.0.0a","1.0.0b","1.0.0.1"];
console.log(sortVersions(v));

【讨论】:

【参考方案17】:

死的简单方法:

function compareVer(previousVersion, currentVersion) 
  const [prevMajor, prevMinor = 0, prevPatch = 0] = previousVersion
    .split(".")
    .map(Number);
  const [curMajor, curMinor = 0, curPatch = 0] = currentVersion
    .split(".")
    .map(Number);

  if (curMajor > prevMajor) 
    return "major";
  
  if (curMinor > prevMinor) 
    return "minor";
  
  if (curPatch > prevPatch) 
    return "patch";
  
  return "same or downgrade version";

输出:

compareVer("3.1", "3.1.1") // patch
compareVer("3.1.1", "3.2") // minor
compareVer("1.1.1", "2.1.1") // major
compareVer("1.1.1", "1.2.1") // minor
compareVer("1.1.1", "1.1.2") // patch
compareVer("1.1.1", "0.1.1") // same or downgrade version

【讨论】:

【参考方案18】:

您可以将String#localeCompareoptions 一起使用

灵敏度

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

"base":只有基本字母不同的字符串比较不相等。示例:a ≠ ba = áa = A"accent":只有在基本字母或重音符号和其他变音符号不同的字符串比较为不相等。示例:a ≠ ba ≠ áa = A"case":只有在基本字母或大小写不同的字符串比较不相等。示例:a ≠ ba = áa ≠ A"variant":基本字母、重音符号和其他变音符号不同的字符串,或者大小写比较不相等。也可以考虑其他差异。示例:a ≠ ba ≠ áa ≠ A

使用“排序”的默认值为“变体”;它依赖于使用“搜索”的语言环境。

数字

是否应使用数字排序规则,例如“1”true 和false;默认为false。此选项可以通过选项属性或通过 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 是什么语言?你怎么能在我读其他人的时候发布这个;) undefined 是语言环境部分,此处未使用。【参考方案19】:

我们现在可以使用Intl.Collator API 创建数字比较器。 Browser support 相当不错,但在撰写本文时 Node.js 不支持。

const semverCompare = new Intl.Collator("en",  numeric: true ).compare;

const versions = ['1.0.1', '1.10.2', '1.1.1', '1.10.1', '1.5.10', '2.10.0', '2.0.1'];

console.log(versions.sort(semverCompare))

const example2 = ["1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"];
console.log(example2.sort(semverCompare))

【讨论】:

【参考方案20】:

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

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

【讨论】:

史诗般的答案,正是我想要的。【参考方案21】:
// 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
    

【讨论】:

欢迎来到 SO。这个问题已经有很多很好的答案了,除非你添加新的东西,否则请不要添加新的答案。【参考方案22】:

replace() 函数仅替换字符串中的第一次出现。所以,让我们用, 替换.。然后删除所有. 并再次将, 变为. 并将其解析为浮动。

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

最后,排序:

versions.sort();

【讨论】:

【参考方案23】:

看看这个blog post。此函数适用于数字版本号。

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

【讨论】:

【参考方案24】:

例如,如果我们要检查当前 jQuery 版本是否小于 1.8,如果版本为“1.10.1”,parseFloat($.ui.version) &lt; 1.8 ) 将给出 错误 结果,因为 parseFloat(" 1.10.1") 返回1.1。 字符串比较也会出错,因为 "1.8" &lt; "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/

【讨论】:

啊,刚刚注意到 ripper234 几个月前在 eof 的 cmets 上发布了一个小提琴 URL,这非常相似。无论如何,我把我的答案留在这里...... 在这些情况下,这个也将失败(与大多数变体一样):versionCompare('1.09', '1.1') 返回“1”,与 versionCompare('1.702', ' 1.8')。 代码评估“1.09”>“1.1”和“1.702”>“1.8”,我认为这是正确的。如果你不同意:你能指出一些支持你观点的资源吗? 这取决于你的原则——据我所知,没有严格的规则之类的。关于资源,“增量序列”中“软件版本控制”的***文章说 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。 我看到有些规则处理 1.8 【参考方案25】:

这是一个巧妙的技巧。如果您正在处理数值,在特定范围的值之间,您可以为版本对象的每个级别分配一个值。例如,“largestValue”在此处设置为 0xFF,这为您的版本控制创建了一种非常“IP”的外观。

这也处理字母数字版本控制(即 1.2a

// 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);

【讨论】:

【参考方案26】:

我根据 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,这对于正常比较来说已经足够了。此处解释错误:How to deal with floating point number precision in 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

【讨论】:

这是一个很好的解决方案。也可以单行:("" + versionString).replace("_", ".").replace(/[^0-9.]/g, "").split(".").reverse().reduce((accumulator, value) =&gt; accumulator/1000 + Number(value), 0)【参考方案27】:

这是一个适合与 Array.sort 一起使用的咖啡脚本实现,灵感来自此处的其他答案:

# 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

【讨论】:

这是受到LeJared's answer的启发。 这不能正常工作..这是结果..结果 ['1.1.1', '2.1.1', '3.3.1.0', '3.1.1.0']【参考方案28】:

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

特点

序列“1.0.1.5.53.54654.114.1.154.45”有效 序列长度没有限制:'1.1546515465451654654654654138754431574364321353734'有效 可以按版本对对象进行排序(参见自述文件) 阶段(如 alpha、beta、rc1、rc2)

如果您需要其他功能,请随时打开问题。

【讨论】:

【参考方案29】:

这适用于以句点分隔的任意长度的数字版本。仅当 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);

【讨论】:

【参考方案30】:

如果您不想允许使用字母或符号,则只需几行代码即可。如果您控制版本控制方案并且它不是第 3 方提供的东西,则此方法有效。

// we presume all versions are of this format "1.4" or "1.10.2.3", without letters
// returns: 1 (bigger), 0 (same), -1 (smaller)
function versionCompare (v1, v2) 
  const v1Parts = v1.split('.')
  const v2Parts = v2.split('.')
  const length = Math.max(v1Parts.length, v2Parts.length)
  for (let i = 0; i < length; i++) 
    const value = (parseInt(v1Parts[i]) || 0) - (parseInt(v2Parts[i]) || 0)
    if (value < 0) return -1
    if (value > 0) return 1
  
  return 0


console.log(versionCompare('1.2.0', '1.2.4') === -1)
console.log(versionCompare('1.2', '1.2.0') === 0)
console.log(versionCompare('1.2', '1') === 1)
console.log(versionCompare('1.2.10', '1.2.1') === 1)
console.log(versionCompare('1.2.134230', '1.2.2') === 1)
console.log(versionCompare('1.2.134230', '1.3.0.1.2.3.1') === -1)

【讨论】:

以上是关于如何使用js比较软件版本号? (只有数字)的主要内容,如果未能解决你的问题,请参考以下文章

js实现软件版本号的比较

华为OD机试 - 获取最大软件版本号(Python)| 真题含思路

华为OD机试 - 获取最大软件版本号(JavaScript) | 机试题算法思路 2023

华为OD机试真题Java实现获取最大软件版本号真题+解题思路+代码(2022&2023)

js 比较版本号

华为OD机试真题 JS 实现获取最大软件版本号2023 Q1 | 100分