像函数 strcmp 一样逐个比较字符

comparing char one by one like function strcmp

本文关键字:一样 比较 字符 函数 strcmp      更新时间:2023-10-16

我正在尝试逐个比较字符。我正在从类的赋值中模拟 strcmp 函数。这是我的想法。不幸的是,我一直得到 0,因为所有字符都匹配,直到它到达最后一个。我假设它只检查第一个字符并停止。我将 i++ 添加到下一个字符中,但我认为它不起作用。

strComp("abc", "abcd");
int strComp(char a[], char b[]) {
    int i = 0;
    if (strLen(a) == strLen(b)) {
        while (a[i] != NULL && b[i] != NULL) {
            if (a[i] == b[i]) {
                return 0;
            } else if(a[i] > b[i]) {
                return 1;
            } else {
                return -1;
            }
        }
        i++;
    } else if (strLen(a) > strLen(b)) {
        return 1;
    } else {
        return -1;
    }
}

注意

  • NULL不同于''
  • char[]真的腐烂成char*
  • 在 C/C++ 中,任何可以const的东西都应声明const
  • 在这样的基本函数中使用strlen效率低下

这是一个非常快速的解决方案:

inline int compare(char const* const a, char const* const b)
{
    /* Return -1 less than, 0 equal, 1 greater than */
    if (!a[0] && b[0])
        return -1;
    else if (a[0] && !b[0])
        return 1;
    register int i = 0;
    for (; a[i] && b[i]; i++) {
        if (a[i] < b[i])
            return -1;
        if (a[i] > b[i])
            return 1;
    }
    #if 1 /* this addition makes this code work like std::strcmp */
    if (!a[i] && b[i])
        return -1;
    else if (a[i] && !b[i])
        return 1;
    #endif
    return 0;
}

这个我在 20 年前编码了更多,作为 386 汇编程序的原型。对于不区分大小写的字符串,比较#include <locale>并修改 for 循环:

            .
            .
        for (; a[i] && b[i]; i++) {
            if (std::toupper(a[i]) < std::toupper(b[i]))
                return -1;
            if (std::toupper(a[i]) > std::toupper(b[i]))
                return 1;
        }

Put

++i; 

在 while 循环内

上面只有 2 行...

只要看到两个不同的字符,就可以检测到不匹配 - 但是在到达字符串末尾并且字符仍然相同之前,您无法检测到匹配项。

至少在我看来,大多数尝试都是向后获得基本思想,试图立即比较字符(一个或两个字符串的一些特殊大小写为空)。相反,通常最好从跳过相等的非零字节开始。然后,您要么位于(至少一个)字符串的末尾,要么发现两个字符串中的字节不匹配。无论哪种方式,此时您都可以整理出正在发生的事情,并返回正确的值。

int cmp_str(char const *a, char const *b) {
    while (*a && *a == *b) {
        ++a;
        ++b;
    }
    if (*b < *a)
       return 1;
    if (*b > *a)
        return -1;
    return 0;    
}

这使得循环非常简单,条件很少,因此可以快速执行。所有更复杂的比较来计算实际顺序都发生在循环之外,它们只发生一次,对速度几乎没有影响。

我可能应该补充一个警告:这并没有试图正确处理国际字符。为此,您只需要添加定义字符相对顺序的整理表,因为(至少在许多代码页中)字符的值与字符的排序顺序不对应。

对于它的价值,这里有一个快速测试,将结果和速度与Andreas的compare和标准库中的strcmp进行比较:

int cmp_str(char const *a, char const *b) {
    while (*a && *a == *b) {
        ++a;
        ++b;
    }
    if (*b < *a)
        return 1;
    if (*b > *a)
        return -1;
    return 0;
}
inline int compare(char const* const a, char const* const b)
{
    /* Return -1 less than, 0 equal, 1 greater than */
    if (!a[0] && b[0])
        return -1;
    else if (a[0] && !b[0])
        return 1;
    register int i = 0;
    for (; a[i] && b[i]; i++) {
        if (a[i] < b[i])
            return -1;
        if (a[i] > b[i])
            return 1;
    }
#if 1 /* this addition makes this code work like std::strcmp */
    if (!a[i] && b[i])
        return -1;
    else if (a[i] && !b[i])
        return 1;
#endif
    return 0;
}
#ifdef TEST
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
int main(){
    char *s1 [] = { "", "a", "one", "two", "three", "one", "final" };
    char *s2 [] = { "x", "b", "uno", "deux", "three", "oneone", "" };
    for (int i = 0; i < 7; i++) {
        printf("%dt", cmp_str(s1[i], s2[i]));
        printf("%dt", compare(s1[i], s2[i]));
        printf("%dn", strcmp(s1[i], s2[i]));
    }
    // Test a long string:
    static const int size = 5 * 1024 * 1024;
    static char s3[size];
    for (int i = 0; i < size - 1; i++)
        s3[i] = (rand() % 254) + 1;
    s3[size - 1] = '';
    static char s4[size];
    strcpy(s4, s3);
    s3[size - 5] = (s3[size - 5] + 4) % 255;
    clock_t start = clock();
    int val1 = cmp_str(s3, s4);
    clock_t t1 = clock() - start;
    start = clock();
    int val2 = compare(s3, s4);
    clock_t t2 = clock() - start;
    start = clock();
    int val3 = strcmp(s3, s4);
    clock_t t3 = clock() - start;
    double v1 = (double) t1 / CLOCKS_PER_SEC;
    double v2 = (double) t2 / CLOCKS_PER_SEC;
    double v3 = (double) t3 / CLOCKS_PER_SEC;
    printf("Jerry: %d, %fnAndreas: %d, %fnstdlib: %d, %fn", val1, v1, val2, v2, val3, v3);
}
#endif

结果:

-1      -1      -1
-1      -1      -1
-1      -1      -1
1       1       1
0       0       0
-1      -1      -1
1       1       1
Jerry: 1, 0.007000
Andreas: 1, 0.010000
stdlib: 1, 0.007000

由于 Andreas 已经更正了他的代码,所有三个测试都产生了相同的结果,但这个版本和标准库比 Andreas 的版本快 30% 左右。不过,这确实因编译器而异。使用 VC++,我的代码几乎与标准库中的代码匹配(但是如果我使用一个巨大的字符串,例如 200 兆字节,则标准库中的版本要好得多。使用 g++,标准库中的代码似乎比 VC++ 标准库中的代码慢一点,但它为 Andreas 的代码或我的代码生成的结果比 VC++ 为他们生成的要差得多。在 200 MB 的字符串上,我使用 VC++ 得到以下结果:

Jerry: 1, 0.288000
Andreas: 1, 0.463000
stdlib: 1, 0.256000

。但是使用 g++ (MinGW),我得到这样的结果:

Jerry: 1, 0.419000
Andreas: 1, 0.523000
stdlib: 1, 0.268000

尽管无论哪种方式的排名都保持不变,但标准库和我的代码之间的速度差异使用 g++ 比使用 VC++ 要大得多。

关于你的代码,我必须说两件事:

  1. i++需要进入循环内部,但你应该使用++i,这取决于编译器i++可能比++i慢。
  2. 它足够while (a[i])而不是while (a[i] != NULL && b[i] != NULL),因为ab长度相等。

只是因为你回来得太早了。在函数中执行返回后,控件将返回到调用它的位置。

在这种情况下,您将在 while 循环中返回,这是一个逻辑错误。让我们在这里举个例子。首先,它将比较 a[0] 和 b[0],它将根据您的代码在所有三种情况下返回。即 a[0]b[0] 返回 1,否则返回 0...你必须改变整个功能。我会根据需要编辑您的函数,请稍候

int strComp(char a[], char b[]) {
    int i = 0;
    if (strLen(a) == strLen(b)) {
        while (a[i] != NULL && b[i] != NULL) {
            if (a[i] == b[i]) {
                return 0;
            } else if(a[i] > b[i]) {
                return 1;
            } else {
                return -1;
            }
        }
        i++;
    } else if (strLen(a) > strLen(b)) {
        return 1;
    } else {
        return -1;
    }
}

编辑的代码(PS:请检查我没有尝试过):

  int strComp(char a[], char b[])
    {
        int i = 0;

            while (a[i]!=''&&b[i]!='')  
            {
             if(a[i] > b[i])
                {
                    return 1;
                }
                else if (a[i] < b[i])
                {
                    return -1;
                }
                i++;   //place i++ here
            }
       if(a[i]==b[i])
          return 0;  //if string are equal
      if(a[i]=='')
          return -1;
      else 
         return 1;
    }