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

- 15,848
- 18
- 99
- 158

- 3,305
- 7
- 44
- 55
-
1http://stackoverflow.com/a/34484221/1318830 Answered there and then found your question – mkungla Dec 27 '15 at 19:54
-
I suggest that create version class instead of string. you might need `1.0.0.9 beta` as well. that's not a simple integer compare. – Sungguk Lim Jan 19 '16 at 01:30
-
2C version of this question for those interested: [comparing version numbers in c](http://stackoverflow.com/questions/15057010) – hippietrail Jan 19 '16 at 01:40
5 Answers
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.

- 104,103
- 58
- 317
- 552
-
3The algorithm is good. I'd suggest wrapping it as a `class Version { Version(std::string const&); bool operator<(Version const& rhs) const; };`. This allows you to have a `std::set
` for instance. – MSalters May 31 '10 at 08:37 -
@Johnsyweb: Thanks for picking up on the typo. @MSalters: I agree. I wasn't saying use this for production -- I was just demonstrating the algorithm I think the OP should use. – Billy ONeal May 31 '10 at 13:32
-
1@Julien: Nope, it'll work with multidigit numbers. (assuming your standard library's operator>> does the "right thing") It does assume that the periods are one character though. – Billy ONeal Aug 06 '12 at 23:28
-
1With a simple change, the code can be made more robust against invalid input and make it possible to compare version numbers with less than 4 fields (if less fields are passed, the unspecified ones can be assumed to be zero). All that's necessary is to always zero-initialize the variables `parsedA` and `parsedB`, like this (C++11ish): `int parsedA[4]{}, parsedB[4]{};` or this (pre C++11): `int parsedA[4] = {0}, parsedB[4] = {0}`. – zett42 Jan 02 '18 at 14:23
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";
}
}

- 257,169
- 86
- 333
- 562
-
1You should use `std::vector
::assign` instead of `std::copy` ;) Otherwise +1. – Billy ONeal May 31 '10 at 13:31 -
Just a little suggestion to make the class more complete, operator-wise. If _boost_ is available, one could [derive from `boost::less_than_comparable`](https://theboostcpplibraries.com/boost.operators) to automatically add `operator>`, `operator<=`, and `operator>=` which are all implemented in terms of `operator<`. Also useful would be `operator==` and to derive from `boost::equality_comparable` to provide `operator!=`. – zett42 Jan 02 '18 at 14:47
-
@zett42. No need for that. To add the comparison operators all you need to do is add `using namespace std::rel_ops`. [see](http://www.cplusplus.com/reference/utility/rel_ops/) – Martin York Jan 02 '18 at 16:52
-
`std::rel_ops` has some issues. First, as stated on the linked page: _"Notice that using this namespace introduces these overloads for all types not defining their own."_ - who knows how that changes behaviour of complex template libraries? Second, usability. Users of your class have to remember to add `using namespace std::rel_ops` which is not the case when adding these operators as class members (either manually or using boost). – zett42 Jan 02 '18 at 17:00
-
@zett42: Sure. But we are looking at adding functionality that is hardly ever used (for the case of Version). So isolating the using class for these exceptional situations is neither difficult or cumbersome (because you basically never need it and when you do its one line). Secondly it is better than forcing inheritance onto a situation where it is not needed. Thirdly if this was a case where the class needed those operators (it made sense using them) then using `std::tuple` (which is a one liner for each). Using `std::tuple` is a lot less heavy wight than forcing inheritance onto a class. – Martin York Jan 02 '18 at 17:33
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; }

- 29,020
- 36
- 159
- 267
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

- 15,723
- 4
- 60
- 67
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;
}

- 15,848
- 18
- 99
- 158

- 7
- 2
-
In this case, 1.0.2 is fresher than 1.1.0 + The index will go out of the range – aLoneStrider May 30 '20 at 00:20