2013-02-24 2 views
5

У меня много ответов на эту проблему на других языках, но я пытаюсь найти способ сравнить 2 номера версий, заданные как строки. НапримерСравнение номеров версий в c

str1 = "141.1.23" 
str2 = "141.1.22" 

Я пытаюсь найти способ сравнения целочисленных значений в строках, чтобы увидеть, какая из них больше. (В этом случае str1 будет больше). Я думал об использовании когда-либо комбинации с atoi и strtok, но я знаю, что я не смогу сразу же выделить 2 строки. Любой совет?

+0

Для этого примера 'strcmp' будет делать :-) – cnicutar

+1

Mmm, stcmp, вероятно, поставит Linux 2.14 перед Linux 2.4. –

+0

@David Grayson: strcmp возвращает значение больше нуля, когда первый символ, который не соответствует, имеет большее значение в str1, чем в str2, а сравнение останавливается на первом нуле, поэтому оно все равно будет работать даже для 2.1 и 2.14 , 2.1 и 2.10 могут быть неоднозначными, но это так или иначе. В этом случае отнюдь не ясно, что представляет собой допустимую строку версии. Он будет работать до тех пор, пока все группы цифр, кроме последней, имеют одинаковую длину, поэтому вы не сравниваете цифру с точкой. – Clifford

ответ

4

Я знаю, что я не смогу tokenize 2 строки сразу.

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

strunct version_t { 
    int major; 
    int minor; 
    int build; 
}; 

version_t parse_ver(const char* version_str) { 
    version_t res; 
    // Use strtok_r to split the string, and atoi to convert tokens to ints 
    return res; 
} 

Теперь вы можете позвонить parse_ver дважды, получить два version_t значения, и сравнить их бок о бок.

P.S. Если вы примете соглашение, чтобы всегда вводить числа с ведущими нулями на определенную длину, то есть убедитесь, что вы пишете "141.1.03", а не "141.1.3", вы можете заменить целочисленное сравнение на лексикографическое.

+0

Что касается предложения фиксированной длины поля, из примера в вопрос, что это не так. В лучшем случае это плохо сказано. – Clifford

3

Следующая процедура сравнивает строки номера версии, которые состоят из подлинных чисел. Преимущество заключается в том, что разделитель не имеет значения; он будет работать, например, с 141.01.03, 141: 1: 3 или даже 141A1P3. Он также обрабатывает несоответствующие хвосты, чтобы 141.1.3 приходилось до 141.1.3.1.

#include <assert.h> 
#include <stdlib.h> 

int versionCmp(char *pc1, char *pc2) 
{ 
    int result = 0; 
    /* loop through each level of the version string */ 
    while (result == 0) { 
     /* extract leading version numbers */ 
     char* tail1; 
     char* tail2; 
     unsigned long ver1 = strtoul(pc1, &tail1, 10); 
     unsigned long ver2 = strtoul(pc2, &tail2, 10); 
     /* if numbers differ, then set the result */ 
     if (ver1 < ver2) 
      result = -1; 
     else if (ver1 > ver2) 
      result = +1; 
     else { 
      /* if numbers are the same, go to next level */ 
      pc1 = tail1; 
      pc2 = tail2; 
      /* if we reach the end of both, then they are identical */ 
      if (*pc1 == '\0' && *pc2 == '\0') 
       break; 
      /* if we reach the end of one only, it is the smaller */ 
      else if (*pc1 == '\0') 
       result = -1; 
      else if (*pc2 == '\0') 
       result = +1; 
      /* not at end ... so far they match so keep going */ 
      else { 
       pc1++; 
       pc2++; 
      } 
     } 
    } 
    return result; 
} 

int main(void) 
{ 
    assert(versionCmp("1.2.3" , "1.2.3") == 0); 
    assert(versionCmp("1.2.3" , "1.2.4") < 0); 
    assert(versionCmp("1.2.4" , "1.2.3") > 0); 
    assert(versionCmp("10.2.4", "9.2.3") > 0); 
    assert(versionCmp("9.2.4", "10.2.3") < 0); 
    /* Trailing 0 ignored. */ 
    assert(versionCmp("01", "1") == 0); 
    /* Any single space delimiter is OK. */ 
    assert(versionCmp("1a2", "1b2") == 0); 
    return EXIT_SUCCESS; 
} 

Заменить strtoul с с strcspn с и strncmp, и вы можете использовать его для сравнения нечисловых версии «номера» - но разделитель должен быть точка. Например, 141.3A.1 сортирует до 141.3B.

... 
while (result == 0) { 
    /* ignore leading zeroes */ 
    pc1 += strspn(pc1, "0"); 
    pc2 += strspn(pc2, "0"); 
    /* extract leading version strings */ 
    int len1 = strcspn(pc1, "."); 
    int len2 = strcspn(pc2, "."); 
    /* if one is shorter than the other, it is the smaller version */ 
    result = len1 - len2; 
    /* if the same length then compare as strings */ 
    if (result == 0) 
     result = strncmp(pc1, pc2, len1); 
    if (result == 0) { 
     pc1 += len1; 
     pc2 += len2; 
     if (*pc1 == '\0' && *pc == '\0') 
      ... 
+0

Для более тщательной проверки целостности вредоносного целого: http://stackoverflow.com/a/12923949/895245 –

2

strverscmp расширение Glibc

Пример:

#define _GNU_SOURCE 
#include <assert.h> 
#include <stdlib.h> 
#include <string.h> 

int main(void) { 
    assert(strverscmp("1.2.3" , "1.2.3") == 0); 
    assert(strverscmp("1.2.3" , "1.2.4") < 0); 
    assert(strverscmp("1.2.3" , "1.2.2") > 0); 
    assert(strverscmp("9.2.3" , "10.2.3") < 0); 
    assert(strverscmp("10.2.3", "9.2.3") > 0); 

    /* Delimiers are also compared. */ 
    assert(strverscmp("1a2", "1b2") < 0); 
    assert(strverscmp("1b2", "1a2") > 0); 

    /* Leading 0s: number gets treated as 0.X, e.g. 01 means 0.1. 
    * Maybe not perfect for version strings, but sane version strings 
    * should not have leading 0s. 
    */ 
    assert(strverscmp("01", "9") < 0); 
    assert(strverscmp("01", "09") < 0); 
    assert(strverscmp("01", "09") < 0); 
    assert(strverscmp("09", "1") < 0); 

    return EXIT_SUCCESS; 
} 

https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strverscmp.c;h=96d4227cd50090f3a7c45e7241d817d34e42f5ce;hb=cbc06bc486635347ee0da51d04a82eedf51602d5#l42

Протестировано на Glibc 2.21, Ubuntu 15.10.

filevercmp от gnulib - еще одна реализация GNU.

Он используется в sort -V из Coreutils 8.23, которая работает следующим образом: https://stackoverflow.com/a/4024263/895245

0

Мы можем использовать strtok как это было предложено. Взгляните на этот код.Чтобы облегчить это, im, используя вектор в C++, используйте другие контейнеры или структуры данных, такие как массив, инициализированный до максимальной длины двух строк, для хранения токенированных элементов.

vector<char*> tokenize(char *s) 
{ 
    vector<char*> svec; 

    char *stp = strtok(s,"."); 
    while(stp != NULL) 
    { 
      svec.push_back(stp); 
      stp = strtok(NULL,"."); 
    } 
    cout << endl; 
    return svec; 

} 

int version_compare(char *s1, char *s2) 
{ 
    vector<char*> tokens_s1 = tokenize(s1); 
    vector<char*> tokens_s2 = tokenize(s2); 

    int st1, st2, flag, maxf,result; 
    st1 = tokens_s1.size(); 
    st2 = tokens_s2.size(); 
    flag = st1 < st2 ? st1 : st2; 


    for(int i=0; i < flag ;i++) 
    { 

      int one = *(tokens_s1[i]); 
      int two = *(tokens_s2[i]); 
      if(one > two) 
        return 1; 
      else if(one < two) 
        return 2; 
      else 
        result = 0; 

    } 
} 

    if((st1 == st2) && (result == 0)) return 0; 
    return (st1 > st2 ? 1 : 2); 



} 


int main() 
{ 
    char s1[] = "1.2.3.4"; 
    char s2[] = "2.2.3.3.3"; 
    int st; 
    st = version_compare(s1,s2); 
    cout<<st<<endl; 

} 
+1

Этот ответ ** C++ **, а не ** C **. – Michi

+0

То, что я указал в заявлении. Я использую только векторы из C++, вы можете заменить их на массивы в C –

+0

. Существует аналогий C++ этого вопроса, где этот ответ может быть более оценен: http://stackoverflow.com/questions/33135019 – hippietrail

0

Минималистская версия C, которая только токенизирует первый несогласованный компонент. Использует strchr() и strtoul().

int version_compare(char *s1, char *s2) 
{ 
    char *delim = ".:-"; 
    while(1) { 
     if (*s1 == *s2) { 
      if (!*s1) 
       return 0; 
      s1++; s2++; 
     } else if (strchr(delim, *s1) || !*s1) { 
      return -1; 
     } else if (strchr(delim, *s2) || !*s2) { 
      return 1; 
     } else { 
      int diff; 
      char *end1, *end2; 
      diff = strtoul(c1, &end1, 10) - strtoul(c2, &end2, 10); 
      if (!diff) { 
       c1 += (end1 - c1); 
       c2 += (end2 - c2); 
      } else { 
       return diff; 
      } 
     } 
    } 
} 
Смежные вопросы