Сравнение номеров версий в c
Я вижу много ответов на эту проблему на других языках, но я пытаюсь найти способ сравнить 2 номера версий, приведенные в виде строк. Например
str1 = "141.1.23"
str2 = "141.1.22"
Я пытаюсь найти способ сравнить целочисленные значения в строках, чтобы увидеть, какое из них больше. (В этом случае str1 будет больше). Я думал об использовании иногда комбинации с atoi и strtok, но я знаю, что не смогу токенизировать 2 строки одновременно. Любой совет?
5 ответов
Я знаю, что не смогу токенизировать 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
значения и сравнить их бок о бок.
PS Если вы принимаете соглашение, чтобы всегда дополняли числа начальными нулями до определенной длины, т.е. убедитесь, что вы пишете "141.1.03"
и не "141.1.3"
Вы можете заменить целочисленное сравнение на лексикографическое.
Мне действительно интересно, почему люди стремятся к таким сложным решениям, когда есть sscanf
в C. Вот очень простое решение этой проблемы, которое будет работать для 99% всех случаев использования:
int compVersions ( const char * version1, const char * version2 ) {
unsigned major1 = 0, minor1 = 0, bugfix1 = 0;
unsigned major2 = 0, minor2 = 0, bugfix2 = 0;
sscanf(version1, "%u.%u.%u", &major1, &minor1, &bugfix1);
sscanf(version2, "%u.%u.%u", &major2, &minor2, &bugfix2);
if (major1 < major2) return 1;
if (major1 > major2) return -1;
if (minor1 < minor2) return 1;
if (minor1 > minor2) return -1;
if (bugfix1 < bugfix2) return 1;
if (bugfix1 > bugfix2) return -1;
return 0;
}
Вот, попробуйте: https://ideone.com/IVz84k
Следующая процедура сравнивает строки с номерами версий, которые состоят из натуральных чисел. Преимущество состоит в том, что разделитель не имеет значения; он будет работать, например, с 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')
...
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;
}
Протестировано на Glibc 2.21, Ubuntu 15.10.
filevercmp
от gnulib
это еще одна реализация GNU.
Используется в sort -V
Coreutils 8.23, который работает следующим образом: /questions/39178209/kak-sravnit-dve-stroki-v-formate-s-razdeleniem-tochkami-v-bash/39178231#39178231
Минималистская версия 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;
}
}
}
}
Мы можем использовать strtok как предложено. Посмотрите на этот код. Чтобы облегчить это, я использую vector в 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;
}