Как разобрать номер версии, чтобы сравнить его?

СЦЕНАРИЙ: у меня есть простое приложение, которое проверяет свой RSS-канал и ищет, доступна ли более новая версия. Таким образом, я хочу проверить, меньше ли текущая версия, чем та, которая присутствует в RSS-канале. В идеале просто:

CURRENTVERSION < updateVersion

ПРОБЛЕМА: версия состоит из major.minor.revision.build, и я не знаю, как преобразовать ее в число для выполнения проверки версии.

Это параметры для сравнения:

#define CURRENTVERSION = 0,2,5,1

Версия, загруженная из Интернета, — "0.2.6.1" (в виде строки).

Как лучше всего проверить, меньше ли одно другого?

Я попытался преобразовать его в двойное, но значение становится равным 0,2 (разбирается только первый ., остальные игнорируются).

ОГРАНИЧЕНИЕ: это не должно быть решение, использующее библиотеки .NET, поскольку приложение должно работать при отсутствии .NET Framework.

(EDIT) Я остановился на следующем решении благодаря ответу Картика Т.

struct Version
{
    Version(string versionStr)
    {
        sscanf(versionStr.c_str(), "%d.%d.%d.%d", &major, &minor, &revision, &build);
    }

    bool operator<(const Version &otherVersion)
    {
        if(major < otherVersion.major)
            return true;
        if(minor < otherVersion.minor)
            return true;
        if(revision < otherVersion.revision)
            return true;
        if(build < otherVersion.build)
            return true;
        return false;
    }

    int major, minor, revision, build;
};

person Amadeus Hein    schedule 17.01.2013    source источник
comment
Сначала вопрос: почему вы #define указываете версию как знак равенства, за которым следует список целых чисел, разделенных запятыми, по сравнению со списком целых чисел, разделенных точками, в веб-версии? Я не вижу там никакого смысла. Вы можете просто #define CURRENTVERSION "0.2.5.1" или даже лучше const char CURRENTVERSION[] = "0.2.5.1";, так как здесь нет необходимости использовать макрос.   -  person Arne Mertz    schedule 17.01.2013
comment
@AmadeusHein Ваш метод сравнения неверен. Например, утверждается, что Version("1.0.0.0") меньше Version("0.2.5.1"). После каждого if(x < otherVersion.x) return true; должно быть if(otherVersion.x < x) return false;.   -  person reima    schedule 31.12.2013
comment
Пожалуйста, не вкладывайте свое решение в свой вопрос; вместо этого добавьте его как ответ. Можно добавить ответ, который является уточнением другого ответа.   -  person Edward Brey    schedule 15.07.2014
comment
stackoverflow.com/a/34484221/1318830   -  person mkungla    schedule 29.12.2015
comment
Ваше решение ошибочно. Код, который у вас есть, попытается обновить с 3.1.2.2 до 2.2.2.2, поскольку, например, младший меньше.   -  person super    schedule 11.07.2018


Ответы (3)


struct Version{
    Version(std::string versionStr);     //remember to use  versionStr.c_str() if using C functions like sscanf or strtok
    bool operator<(const Version &other); // could and perhaps should be a free function

    int major,minor,revision,build;
};


bool needtoUpdate = Version(CURRENTVERSION)<Version(latestVersion);

Пожалуйста, заполните определения.

Кроме того, ваш #define является ошибкой. Вы хотите, как показано ниже. Или используйте const char *, если можете.

#define CURRENTVERSION "0.2.5.1"

Вы можете использовать sscanf или что-то вроде strtok для анализа в конструкторе.

person Karthik T    schedule 17.01.2013
comment
Спасибо! Это выглядит великолепно. Я очень новичок в C++ (как я уверен, вы поняли :)), не могли бы вы также ужасно показать синтаксический анализ конструктора? - person Amadeus Hein; 17.01.2013
comment
Зачем ему вообще #define это, когда он может (и, следовательно, должен) использовать константную переменную? - person Arne Mertz; 17.01.2013
comment
Я бы предпочел оставить это вам в качестве упражнения :) Найдите токенизированную строку C++ или посмотрите на cplusplus.com/reference/cstring/strtok - person Karthik T; 17.01.2013
comment
Я не знаю, лучший ли это способ, но это приложение является оболочкой для приложения, зависящего от .NET, и есть сценарий сборки, изменяющий CURRENTVERSION (на приложение .NET). Я подумал, что проще всего будет изменить #define. - person Amadeus Hein; 17.01.2013
comment
@ArneMertz в основном ограничивает мои изменения в вопросе, предпочтительнее const char *, но в таких случаях я бы предпочел определить, поскольку мне нужно будет определить переменную в .cpp, чтобы избежать множественных определений и т. д. - person Karthik T; 17.01.2013
comment
@KarthikT Думаю, это к лучшему :) Еще раз спасибо! - person Amadeus Hein; 17.01.2013
comment
@KarthikT: константы в области пространства имен имеют внутреннюю связь в C++, поэтому вы можете определить их внутри заголовка и не иметь нарушения ODR. Таким образом, const char[] подойдет так же, как const char * const (обратите внимание, что указатель должен быть константным, чтобы получить внутреннюю связь). Если вы предпочитаете делать внутреннюю связь явной, вы можете либо объявить ее static (стиль C), либо внутри безымянного пространства имен (стиль C++). действительно нет оправдания использованию #define для констант в C++ - person Arne Mertz; 17.01.2013
comment
@ArneMertz Я не знал об этом правиле. В таком случае да const char * имеет больше смысла. - person Karthik T; 17.01.2013

Я рекомендую просто использовать функцию, аргумент которой состоит из двух строк, обозначающих номер версии. Нет необходимости использовать структуру или класс, чтобы сделать такую ​​простую вещь. Я думаю, что лучше держать вещи простыми. Например:

#include <stdio.h>
#include <string.h>

/*
 * return 1 if v1 > v2
 * return 0 if v1 = v2
 * return -1 if v1 < v2
 */

int cmpVersion(const char *v1, const char *v2)
{
    int i;
    int oct_v1[4], oct_v2[4];
    sscanf(v1, "%d.%d.%d.%d", &oct_v1[0], &oct_v1[1], &oct_v1[2], &oct_v1[3]);
    sscanf(v2, "%d.%d.%d.%d", &oct_v2[0], &oct_v2[1], &oct_v2[2], &oct_v2[3]);

    for (i = 0; i < 4; i++) {
        if (oct_v1[i] > oct_v2[i])
            return 1;
        else if (oct_v1[i] < oct_v2[i])
            return -1;
    }

    return 0;
}

int main()
{
    printf("%d\n", cmpVersion("0.1.2.3", "0.2.3.4"));
}
person Bin Wang    schedule 17.01.2013
comment
Использование структуры имеет нулевую нагрузку на производительность и может даже улучшить производительность в простом случае сортировки вектора версий, в отличие от хранения вектора строк, представляющих версию. Кроме того, нет необходимости использовать структуру или класс, чтобы сделать такую ​​простую вещь, просто игнорируя всю концепцию типов и большие преимущества системы типов. Простой пример — невозможность передать строку, не представляющую версию, в функцию, которая ожидает версию. - person ZivS; 14.06.2021

Если у вас есть компилятор, поддерживающий C++11, вы можете std::tuple сделать код чище:

 struct Version
  {
      Version(std::string versionStr)
      {
          sscanf(versionStr.c_str(), "%d.%d.%d.%d", &major, &minor, &revision, &build);
      }
      VersionTuple ToTuple() const
      {
          return VersionTuple{ major,minor,revision,build };
      }
      int major, minor, revision, build;
  };

  bool operator<(const Version& v1, const Version& v2)
  {
      return v1.ToTuple() < v2.ToTuple();
  }
person ZivS    schedule 13.02.2019