Каков наилучший гибкий способ сравнения номеров версий?

Я работаю со сценарием для сравнения номеров версий установленных и доступных приложений. Обычно я бы использовал простые операторы сравнения. Поскольку я создаю это приложение в среде PHP 5.3, я рассмотрел возможность использования version_compare (), но, похоже, это не так чисто соответствует моим потребностям, как мне хотелось бы.

Строки версии, которые я сравниваю, могут соответствовать многим форматам, но до сих пор я встречал следующие:

  • 2.6.18-164.6.1.el5 по сравнению с 2.6.18-92.1.13.el5
  • '4.3p2' против '5.1p1'
  • "5.1.6" по сравнению с "5.2.12"
  • 2.6.24.4-foo.bar.x.i386 по сравнению с 2.4.21-40

Как видите, у меня действительно нет единого формата, с которым я мог бы работать.

Единственное, что я решил сделать, - это разделить каждую строку версии на нечисловые символы, затем повторить итерацию результирующих массивов и сравнить относительные индексы. Однако я не уверен, что это будет хороший способ сделать это, особенно в случае «2.6.24-4-foo.a.12.i386» по сравнению с «2.6.24-4-foo.b.12». .i386 '.

Существуют ли какие-либо хорошо протестированные методы сравнения таких очень нечетких номеров версий, как этот, особенно в среде PHP?


person Skudd    schedule 01.03.2010    source источник
comment
Как насчет того, чтобы вместо этого использовать символы, не являющиеся словами? Или только соответствующие разделители?   -  person Gumbo    schedule 02.03.2010
comment
Вам придется объяснить немного подробнее. Самая большая проблема, с которой я столкнулся, заключается в том, что некоторые номера версий имеют разный формат в зависимости от установленного пакета (например, ядра Linux) и того, кто его создал.   -  person Skudd    schedule 02.03.2010


Ответы (2)


Разделение по символу (см. preg_split) и численное сравнение каждого элемента (если оба являются числовыми) или с использованием сравнения строк (когда оба являются буквенно-цифровыми) работает для ваших примеров:

    '2.6.18-164.6.1.el5' > '2.6.18-92.1.13.el5'
    2  6  18  164 6  1  e15
    2  6  18  92  1  13 e16 // higher
              ^ 

    '4.3p2' < '5.1p1'
    4 3 p2
    5 1 p1 // higher
    ^

    '5.1.6' < '5.2.12'
     5  1  6
     5  2  12 // higher
        ^

    '2.6.24.4-foo.bar.x.i386' > '2.4.21-40'
     2  6  24  4   foo  bar  x  i386 // higher
     2  4  21  40  ---  ---  -  ---- 
        ^

Там, где он потенциально падает, это версия вроде 5.2-alpha-foo vs 5.2.49.4-beta-bar, где вы должны сравнить чисто числовую подстроку с буквенно-цифровой подстрокой:

    5.2-alpha-foo > 5.2.49.9.-beta-bar
    5  2  alpha  foo ----  ---  // wrong - ascii 65(a) vs 52(4)
    5  2  49     4   beta  bar
          ^

Вы можете решить эту проблему, рассматривая буквенно-цифровое поле как 0 каждый раз, когда у вас есть чисто числовая подстрока по сравнению с буквенно-цифровой подстрокой.

person meagar    schedule 01.03.2010
comment
Хм, ну, я попробовал несколько разных примеров против результата preg_split ('/ [^ 0-9a-z] / i', $ foo) и preg_split ('/ [^ 0-9a-z] / i', $ bar) кажется, что он работает, даже с учетом упомянутой вами ошибки. Мне нужно провести еще несколько тестов, но, возможно, я выберу этот путь. - person Skudd; 02.03.2010

Для справки rpm сравните строки версий примерно так:

  • Разделить на все не буквенно-цифровые символы
  • Сгруппируйте последовательные числовые символы вместе и все нечисловые символы вместе (например, 1.12.ab002 разделен на 1, 12, ab, 002)
  • Compare each group left to right
    • if both versions have numeric group they are compared as numbers (i.e. 1 = 001 and 12 > 5)
    • если какая-либо группа не является числовой, выполняется простое сравнение строк
  • Результатом является первое неравное сравнение.
  • Более длинные версии считаются большими (например, 1.2.3 ‹1.2.3.0 и alp‹ альфа)

У этого есть недостатки: 1.2.3rc1> 1.2.3 и 1.2.3alpha> 1.2.3, которые могут быть неправильными.

person Craig    schedule 01.03.2010