如何在C++中比较版本号

6
我们的教授希望我们编写一个程序来比较两个版本号,例如0.1 < 0.2或1 < 1.1。还有一些特殊情况,例如.0.4 < .1。
因此,我的想法是首先判断数字是否以点号开头,如果是,则在它前面添加一个0。然后删除除第一个点号之外的所有其他点号。然后将字符串转换为数字并进行比较。这是我在第一步中执行的操作。
string numb1,numb2;
if(numb1[0]=='.')
{
    numb1 ="0"+ numb1;
}

我对第二个数字做了同样的操作。现在我需要帮助来告诉我如何除了第一个点以外删除其他点。 我们的教授要求我们使用特定的函数:int compareVersions(string ver1, string ver2)。 如果ver1> ver2:返回1 如果ver1
顺便说一下,一些版本号可能非常长,例如2.3.2.2.3.1.1.5.3.5.6.2或1.1.1.1.1.1.1。

请使用编辑功能添加有关您的问题的其他信息 :) - DawidPi
请写下您所需方法的(1)签名,(2)示例输入和(3)示例输出。 - qqqqq
4
不,你不能删除点,因为你有像1.11.2.22和11.1.22.2这样的内容。 - n. m.
分割并计算点的数量呢?如果相同,则比较第一个,以此类推? - rahpuser
我认为我可以除了第一个点之外删除其他的点。我考虑过通过点来分割字符串,并比较它们的每个部分,但是如果它们没有相同的点呢? - Huang Xu
对于那些感兴趣的人,这是C语言版本的问题:在C中比较版本号 - hippietrail
5个回答

5
这里提供一种针对数字版本号的方法:
  • 使用getline(strstream, token, ".")将输入字符串分割成若干部分。
  • 使用atoistol将对应的部分转换为数字并进行数值比较。
基本上,将版本号视为数字序列,由.分隔开,并按字典顺序进行比较。
需要注意的是,实际上一个通用版本号比较算法可能需要处理额外的复杂情况,如字母后缀(例如1.1e2.4b243.5rc1等)。我假设这已经超出了课堂练习的范围,但是方法类似:将这些部分分成数字和非数字部分的序列,然后逐个进行比较(例如2.4b7 < 2.4b24,因为4、"b"、7 < 4、"b"、24)。

可能使用std::istringstream而不是strtok来分割会更容易。 - Slava
@Slava:好注意,我没看到它被标记为 C ++。使用 getline 是一个更好的主意。 - nneonneo
boost字符串分割在这里非常合适。请看我的答案。 - Chiel

2

类似这样的代码可以用来进行检查,而且非常简洁。它利用boost库来分割字符串,然后逐步比较版本号。它会自动处理缺少前导零的情况。

#include <boost/algorithm/string.hpp>
#include <string>
#include <vector>
#include <iostream>

int version_a_newer_than_b(const std::string& a, const std::string& b)
{
    // First, split the string.
    std::vector<std::string> va, vb;
    boost::split(va, a, boost::is_any_of("."));
    boost::split(vb, b, boost::is_any_of("."));

    // Compare the numbers step by step, but only as deep as the version
    // with the least elements allows.
    const int depth = std::min(va.size(), vb.size());
    int ia,ib;
    for (int i=0; i<depth; ++i)
    {
        ia = atoi(va[i].c_str());
        ib = atoi(vb[i].c_str());
        if (ia != ib)
            break;
    }

    // Return the required number.
    if (ia > ib)
        return 1;
    else if (ia < ib)
        return -1;
    else
    {
        // In case of equal versions, assumes that the version
        // with the most elements is the highest version.
        if (va.size() > vb.size())
            return 1;
        else if (va.size() < vb.size())
            return -1;
    }

    // Everything is equal, return 0.
    return 0;
}

int main()
{
    std::string a = "0.1.32.8";
    std::string b = "0.1";

    std::cout << "Is a newer than b: " << version_a_newer_than_b(a, b) << std::endl;

    return 0;
}

我正在阅读它!非常感谢! - Huang Xu
我无法在我的编译器中使用boost/algorithm/string.hpp库。有什么办法可以解决这个问题吗? - Huang Xu
@JPhi1618。SO是为了更好的编程,而不是为了解决作业问题。因此,我选择使用boost。 - Chiel
@HuangXu Boost不是标准库,必须下载、编译和正确安装才能使用。这对于更高级的C++用户来说是一个很好的答案,但可能对你的课程没有帮助。你的教授可能甚至不允许你使用Boost。 - JPhi1618
谢谢你们两个!至少你们给了我一些想法。 - Huang Xu
显示剩余4条评论

0

由于您知道numb[1]等于'.',因此您可以直接使用

 numb1.erase(std::remove(numb1.begin() + 2, numb1.end(), '.'), numb1.end());

这将删除 numb1 中第二个字符后的所有点。


1
删除点确实是OP所要求的,但我认为这是错误的方法。请查看n.m.的评论。 - Fabio says Reinstate Monica
1
但是有些输入可能像 000.1,那么 numb[1] 就不等于 '.'。 - Huang Xu
当然,我完全同意这一点,我只是回答了 OP 的具体问题。 - sasha.sochka

0
你需要做的是遍历字符串,忽略 '.' 并将数字的字符表示转换为整数。然后比较两个最终结果。
string numb1 = "4.3.2";
string numb2 = "3.4.5";
int n1 = 0;
int n2 = 0; 

for (int i = 0; i < numb1.length(); i++)
{
    if (numb1[i] != '.')
    {   
        n1 = n1 * 10;
        n2 = n2 * 10;
        n1 += (int(numb1[i]) - '0');
        n2 += (int(numb2[i]) - '0');                
    }

}

那将会给你432和345,比较它们将会告诉你哪个是更高版本。

1
我认为你不能忽略点号。你必须保留第一个点号,否则2.1.1将与.2.1.1相同。 - Huang Xu
你的想法基本上是将版本号比作十进制数字。但是你的算法在任何高于9的数字上都会失败(因此无法正确地处理为十进制数字的数字)。例如,我现在正在使用Chrome 45.0.2454.101。使用你的算法,这个版本将被认为比45.1.0.0甚至46.0.0.0更高。要修复算法,你需要使用2455进制(比最高数字多一个),也就是说,你需要用n1 = n1 * 2455;替换n1 = n1 * 10;,但是要找出这个数字,你必须先解析两个版本。 - Fabio says Reinstate Monica
即使我进行了更改,如果它们具有不同数量的点(例如45.3与46),您将如何比较版本?您必须首先将46转换为46.0。总的来说,这是很多工作,而且不值得。 - Fabio says Reinstate Monica
2.11将等于2.1.1,所以这种方法只适用于单个数字部分。 - undefined

-1

以下示例将演示以下版本格式之间的比较: major.minor.revision.build或任何更短的版本,例如仅major,同时允许您扩展它以适应您的需求,如下所示,

"某些版本号可能非常长,例如2.3.2.2.3.1.1.5.3.5.6.2"

使用下面的示例,版本字符串开头和结尾的点已经处理好了,因为.0.4被认为等于0.0.4,而.1.被认为等于0.1.0

CompareVersion.h

#ifndef COMPAREVERSION_H_
#define COMPAREVERSION_H_
#include <cstdio>
#include <string>
#include <iostream>
using namespace std;
struct CompareVersion {
public:
    int maj;
    int min;
    int rev;
    int build;
    CompareVersion(std::string);
    bool operator < (const CompareVersion&);
    bool operator <= (const CompareVersion&);
    bool operator == (const CompareVersion&);
    friend std::ostream& operator << (std::ostream& stream, const CompareVersion& ver) {
        stream << ver.maj;
        stream << '.';
        stream << ver.min;
        stream << '.';
        stream << ver.rev;
        stream << '.';
        stream << ver.build;
        return stream;
    };
    void reset();
};
#endif /* COMPAREVERSION_H_ */

CompareVersion.cpp

#include "CompareVersion.h"
CompareVersion::CompareVersion(std::string version) 
{
    reset();
    if (version.compare(0,1,".") == 0)
        version = "0"+version;
    if (version.compare(version.size()-1,1,".") == 0)
        version.append("0");
    sscanf(version.c_str(), "%d.%d.%d.%d", &maj, &min, &rev, &build);
    if (maj <= 0) maj = 0;
    if (min <= 0) min = 0;
    if (rev <= 0) rev = 0;
    if (build <= 0) build = 0;
}
bool CompareVersion::operator < (const CompareVersion& other)
{
    if (maj < other.maj) return true;
    if (min < other.min) return true;
    if (rev < other.rev) return true;
    if (build < other.build) return true;

    return false;
}
bool CompareVersion::operator <= (const CompareVersion& other)
{
    if (maj >= other.maj) return true;
    if (min >= other.min) return true;
    if (rev >= other.rev) return true;
    if (build >= other.build) return true;

    return false;
}
bool CompareVersion::operator == (const CompareVersion& other)
{
    return maj == other.maj
    && min == other.min
    && rev == other.rev
    && build == other.build;
}
void CompareVersion::reset()
{
    maj = 0;
    min = 0;
    rev = 0;
    build = 0;
}

main.cpp

#include <iostream>
#include "CompareVersion.h"
using namespace std;
int main() 
{
    if((CompareVersion("1.2.3.4") == CompareVersion("1.2.3.4")) == true)
    cout << "Version 1.2.3.4 and version 1.2.3.4 are equal" << endl;

    if((CompareVersion("1.2.3.3") < CompareVersion("1.2.3.4")) == true)
    cout << "Version 1.2.3.3 is smaller than 1.2.3.4. " << endl;

    if((CompareVersion("1.2.3.4") < CompareVersion("1.2.3.4")) == true)
    cout << "You won't see that. " << endl;

    if((CompareVersion("1.2.3.4") <= CompareVersion("1.2.3.4")) == true)
    cout << "Version 1.2.3.4 is smaller or equal to 1.2.3.4" << endl;
    if((CompareVersion("1") <= CompareVersion("1.0.0.1")) == true)
    cout << "Version 1 is smaller or equal to 1.0.0.1" << endl;
    /* THE DOTS */
    if((CompareVersion(".0.4") == CompareVersion("0.0.4")) == true)
    cout << "Version .0.4 and version 0.0.4 are equal" << endl;
    if((CompareVersion(".1.") == CompareVersion("0.1.0")) == true)
    cout << "Version .1. and version 0.1.0 are equal" << endl;
    if((CompareVersion("1") == CompareVersion("1.0.0.0")) == true)
    cout << "Version 1 and version 1.0.0.0 are equal" << endl;
    return 0;
}

输出

Version 1.2.3.4 and version 1.2.3.4 are equal
Version 1.2.3.3 is smaller than 1.2.3.4. 
Version 1.2.3.4 is smaller or equal to 1.2.3.4
Version 1 is smaller or equal to 1.0.0.1
Version .0.4 and version 0.0.4 are equal
Version .1. and version 0.1.0 are equal
Version 1 and version 1.0.0.0 are equal

1
这有漏洞。请考虑 CompareVersion("2.3") <= CompareVersion("2.2")CompareVersion("2.3") <= CompareVersion("1.4")。两个表达式都是真的,但应该是假的。 - metal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接