2013-02-24 25 views
5

Vedo molte risposte a questo problema in altre lingue, ma sto cercando di trovare un modo per confrontare 2 numeri di versione forniti come stringhe. Per esempioconfronto numeri di versione in c

str1 = "141.1.23" 
str2 = "141.1.22" 

Sto cercando di trovare un modo per confrontare i valori interi nelle stringhe per vedere quale è più grande. (In questo caso str1 sarebbe più grande). Ho pensato di usare un po 'di combinazione con atoi e strtok ma so che non sarò in grado di sincronizzare 2 stringhe contemporaneamente. Qualche consiglio?

+0

Per questo esempio, 'strcmp' farebbe :-) – cnicutar

+1

Mmm, stcmp probabilmente inserirà Linux 2.14 prima di Linux 2.4. –

+0

@David Grayson: strcmp restituisce un valore maggiore di zero quando il primo carattere che non corrisponde ha un valore maggiore in str1 che in str2, e il confronto si ferma sul primo nul, quindi funzionerà ancora, anche per 2.1 e 2.14 . 2.1 e 2.10 potrebbero essere ambigui, ma poi è in ogni modo. In questo caso non è affatto chiaro dagli esempi cosa costituisce una stringa di versione valida. Funzionerà finché tutti i gruppi di cifre tranne l'ultimo avranno la stessa lunghezza, quindi non si finirà per confrontare una cifra con un punto. – Clifford

risposta

4

So che non riuscirò a tokenize 2 stringhe in una volta.

Fortunatamente, non c'è bisogno di: fare una funzione che prende una stringa, e lo analizza per tre numeri interi che utilizzano strtok_r (utilizzare una versione rientrante, è molto più sicuro).

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; 
} 

Ora è possibile chiamare parse_ver due volte, ottenere due version_t valori e confrontarli side-by-side.

P.S. Se si adotta una convenzione per riempire sempre i numeri con zeri iniziali su una lunghezza specifica, ovvero assicurarsi di scrivere "141.1.03" e non "141.1.3", è possibile sostituire il confronto tra interi con quello lessicografico.

+0

Per quanto riguarda il suggerimento di lunghezza del campo fisso, non è chiaro dall'esempio in la domanda che questo non è il caso. È al meglio male specificato. – Clifford

3

La seguente routine confronta le stringhe di numeri di versione costituite da numeri originali. Il vantaggio è che il delimitatore non ha importanza; funzionerà con, ad esempio, 141.01.03, 141: 1: 3 o addirittura 141A1P3. Gestisce anche code non corrispondenti in modo che 141.1.3 venga prima di 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; 
} 

Sostituire i strtoul s con strcspn s ed un strncmp, e si può usare per confrontare la versione non numerici "numeri" - ma il delimitatore deve essere un punto. Ad esempio, 141.3A.1 ordina prima di 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

Per un controllo dell'input intero malizioso più completo: http://stackoverflow.com/a/12923949/895245 –

2

strverscmp estensione glibc

Esempio:

#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; 
} 

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

Testato su Glibc 2.21, Ubuntu 15.10.

filevercmp da gnulib è un'altra implementazione GNU.

E 'utilizzato nel sort -V di Coreutils 8.23, che funziona così: https://stackoverflow.com/a/4024263/895245

0

Possiamo usare strtok come suggerito. Dai un'occhiata a questo codice.Per facilitarne l'uso, im usando il vettore in C++, per favore usa altri contenitori o strutture dati come l'array inizializzato al massimo della lunghezza delle due stringhe per contenere gli elementi tokenizzati.

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

Questa risposta è ** C++ ** e non ** C **. – Michi

+0

Quello che ho indicato nella dichiarazione. Sto usando solo i vettori di C++, è possibile sostituirlo con gli array in C –

+0

Esiste una controparte C++ di questa domanda in cui questa risposta potrebbe essere più apprezzata: http://stackoverflow.com/questions/33135019 – hippietrail

0

Una versione C minimalista che solo tokenizza il primo componente non corrispondente. Utilizza strchr() e 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; 
      } 
     } 
    } 
}