\"1.0.0.10\", but it\'s not correct." />
开发者

Compare versions as strings

Comparing version numbers as strings is not so easy...

开发者_运维技巧 "1.0.0.9" > "1.0.0.10", but it's not correct.

The obvious way to do it properly is to parse these strings, convert to numbers and compare as numbers. Is there another way to do it more "elegantly"? For example, boost::string_algo...


I don't see what could be more elegant than just parsing -- but please make use of standard library facilities already in place. Assuming you don't need error checking:

void Parse(int result[4], const std::string& input)
{
    std::istringstream parser(input);
    parser >> result[0];
    for(int idx = 1; idx < 4; idx++)
    {
        parser.get(); //Skip period
        parser >> result[idx];
    }
}

bool LessThanVersion(const std::string& a,const std::string& b)
{
    int parsedA[4], parsedB[4];
    Parse(parsedA, a);
    Parse(parsedB, b);
    return std::lexicographical_compare(parsedA, parsedA + 4, parsedB, parsedB + 4);
}

Anything more complicated is going to be harder to maintain and isn't worth your time.


I would create a version class.
Then it is simple to define the comparison operator for the version class.

#include <iostream>
#include <sstream>
#include <vector>
#include <iterator>

class Version
{
    // An internal utility structure just used to make the std::copy in the constructor easy to write.
    struct VersionDigit
    {
        int value;
        operator int() const {return value;}
    };
    friend std::istream& operator>>(std::istream& str, Version::VersionDigit& digit);
    public:
        Version(std::string const& versionStr)
        {
            // To Make processing easier in VersionDigit prepend a '.'
            std::stringstream   versionStream(std::string(".") + versionStr);

            // Copy all parts of the version number into the version Info vector.
            std::copy(  std::istream_iterator<VersionDigit>(versionStream),
                        std::istream_iterator<VersionDigit>(),
                        std::back_inserter(versionInfo)
                     );
        }

        // Test if two version numbers are the same. 
        bool operator<(Version const& rhs) const
        {
            return std::lexicographical_compare(versionInfo.begin(), versionInfo.end(), rhs.versionInfo.begin(), rhs.versionInfo.end());
        }

    private:
        std::vector<int>    versionInfo;
};

// Read a single digit from the version. 
std::istream& operator>>(std::istream& str, Version::VersionDigit& digit)
{
    str.get();
    str >> digit.value;
    return str;
}


int main()
{
    Version     v1("10.0.0.9");
    Version     v2("10.0.0.10");

    if (v1 < v2)
    {
        std::cout << "Version 1 Smaller\n";
    }
    else
    {
        std::cout << "Fail\n";
    }
}


First the test code:

int main()
{
    std::cout << ! ( Version("1.2")   >  Version("1.3") );
    std::cout <<   ( Version("1.2")   <  Version("1.2.3") );
    std::cout <<   ( Version("1.2")   >= Version("1") );
    std::cout << ! ( Version("1")     <= Version("0.9") );
    std::cout << ! ( Version("1.2.3") == Version("1.2.4") );
    std::cout <<   ( Version("1.2.3") == Version("1.2.3") );
}
// output is 111111

Implementation:

#include <string>
#include <iostream>

// Method to compare two version strings
//   v1 <  v2  -> -1
//   v1 == v2  ->  0
//   v1 >  v2  -> +1
int version_compare(std::string v1, std::string v2)
{
    size_t i=0, j=0;
    while( i < v1.length() || j < v2.length() )
    {
        int acc1=0, acc2=0;

        while (i < v1.length() && v1[i] != '.') {  acc1 = acc1 * 10 + (v1[i] - '0');  i++;  }
        while (j < v2.length() && v2[j] != '.') {  acc2 = acc2 * 10 + (v2[j] - '0');  j++;  }

        if (acc1 < acc2)  return -1;
        if (acc1 > acc2)  return +1;

        ++i;
        ++j;
    }
    return 0;
}

struct Version
{
    std::string version_string;
    Version( std::string v ) : version_string(v)
    { }
};

bool operator <  (Version u, Version v) {  return version_compare(u.version_string, v.version_string) == -1;  }
bool operator >  (Version u, Version v) {  return version_compare(u.version_string, v.version_string) == +1;  }
bool operator <= (Version u, Version v) {  return version_compare(u.version_string, v.version_string) != +1;  }
bool operator >= (Version u, Version v) {  return version_compare(u.version_string, v.version_string) != -1;  }
bool operator == (Version u, Version v) {  return version_compare(u.version_string, v.version_string) ==  0;  }

https://coliru.stacked-crooked.com/a/7c74ad2cc4dca888


Here's a clean, compact C++20 solution, using the new spaceship operator <=>, and Boost's string split algorithm.

This constructs and holds a version string as a vector of numbers - useful for further processing, or can be disposed of as a temporary. This also handles version strings of different lengths, and accepts multiple separators.

The spaceship operator lets us provide results for <, > and == operators in a single function definition (although the equality has to be separately defined).

#include <compare>
#include <boost/algorithm/string.hpp>

struct version {
  std::vector<size_t> data;

  version() {};
  version(std::string_view from_string) {
    /// Construct from a string
    std::vector<std::string> data_str;
    boost::split(data_str, from_string, boost::is_any_of("._-"), boost::token_compress_on);
    for(auto const &it : data_str) {
      data.emplace_back(std::stol(it));
    }
  };

  std::strong_ordering operator<=>(version const& rhs) const noexcept {
    /// Three-way comparison operator
    size_t const fields = std::min(data.size(), rhs.data.size());

    // first compare all common fields
    for(size_t i = 0; i != fields; ++i) {
      if(data[i] == rhs.data[i]) continue;
      else if(data[i] < rhs.data[i]) return std::strong_ordering::less;
      else return std::strong_ordering::greater;
    }

    // if we're here, all common fields are equal - check for extra fields
    if(data.size() == rhs.data.size()) return std::strong_ordering::equal; // no extra fields, so both versions equal
    else if(data.size() > rhs.data.size()) return std::strong_ordering::greater; // lhs has more fields - we assume it to be greater
    else return std::strong_ordering::less; // rhs has more fields - we assume it to be greater
  }

  bool operator==(version const& rhs) const noexcept {
    return std::is_eq(*this <=> rhs);
  }
};

Example usage:

  std::cout << (version{"1.2.3.4"} <  version{"1.2.3.5"}) << std::endl; // true
  std::cout << (version{"1.2.3.4"} >  version{"1.2.3.5"}) << std::endl; // false
  std::cout << (version{"1.2.3.4"} == version{"1.2.3.5"}) << std::endl; // false
  std::cout << (version{"1.2.3.4"} >  version{"1.2.3"})   << std::endl; // true
  std::cout << (version{"1.2.3.4"} <  version{"1.2.3.4.5"}) << std::endl; // true


int VersionParser(char* version1, char* version2) {

    int a1,b1, ret; 
    int a = strlen(version1); 
    int b = strlen(version2);
    if (b>a) a=b;
    for (int i=0;i<a;i++) {
            a1 += version1[i];
            b1 += version2[i];
    }
    if (b1>a1) ret = 1 ; // second version is fresher
    else if (b1==a1) ret=-1; // versions is equal
    else ret = 0; // first version is fresher
    return ret;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜