按字母顺序排序数组,大写字母始终优先

时间:2017-02-06 17:50:39

标签: c arrays sorting

我想按字母顺序对数组进行排序,但我希望首先使用大写字母。我已经实现的是一种简单的排序,它不考虑字母的大小。我应该为它设置一个特殊条件吗?

修改

这是我想要实现的目标: realloc应按如下方式排序:AaaAdDcCFfgGhHI

AAaaCcDdFfGgHhI

5 个答案:

答案 0 :(得分:2)

我们应该从修复代码中的一些问题开始。首先,您需要添加#include <ctype.h>。您已声明char *wsk;,并且没有明显原因分配wsk = s1;。更重要的是,这些是不兼容的类型,因为s1是指向15 char的数组的指针。更重要的是,s1 应该是16个char的数组!您忘记在字符数组中包含'\0'终止符的空格。因此,s1的声明需要成为:

char s1[N][16] = { { "azghtdffopAsAfp" },
                   { "poiuyjklhgADHTp" },
                   { "hgjkFfGgBnVUuKk" },
                   { "lokijuhygtfrdek" },
                   { "AaaAdDcCFfgGhHI" } };

可以改进对qsort()的调用。而不是使用幻数15,最好将字符串的长度存储在变量中。此外,sizeof(char)始终为1:

for (i = 0; i<N; i++) {
    size_t s1_len = strlen(s1[i]);
    qsort(s1[i], s1_len, 1, compare);
}

compare()函数本身中,您需要更改为:

const unsigned char *a1 = w1;
const unsigned char *a2 = w2;

const的强制转换将避免有关丢弃const限定符的警告。转换为unsigned可避免未定义的行为,因为ctype.h函数期望int参数可表示为unsigned char或等于EOF。此外,register是类型限定符:它需要限定类型。所以你需要register int r = ...

但是你的函数也依赖于标准不能保证的执行字符集编码的属性:字母按字母顺序编码。您已使用tolower()函数迈出了实现可移植性的第一步,而不是添加幻数来更改字符的大小写。通过使用isupper()islower()来测试字符的大小写,并使用strcoll()来测试字符的排序,我们可以实现接近最大可移植性的功能。 strcoll()如果适合于语言环境,则会自动在小写字母前输入大写字母,但似乎所有大写字母都在小写字母之前,因此在转换为小写字母后,需要进行显式测试以对两个字符进行比较。要克服的一个障碍是strcoll()比较字符串以进行词汇排序。要使用它来比较字符,我们可以部署复合文字:

register int r = strcoll((const char[]){tolower(*c1), '\0'},
                         (const char[]){tolower(*c2), '\0'});

你的compare()函数中有一个对我没用的循环。 compare()函数应该比较两个char;没有必要循环任何东西,所以我已经删除了这个循环。

我写了一个新的compare()函数,它使用strcoll()和复合文字来比较两个char。如果两个字符比较相等(最大为大小写),则检查它们的大小写。如果情况不同,则大写字符将位于小写字符之前。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>                 // added for strlen() and strcoll()
#include <ctype.h>                  // must add this

#define N 5

int compare(const void *w1, const void *w2);

int main(void) {
    /* Inner dimension should be 16 to include '\0' */
    char s1[N][16] = { { "azghtdffopAsAfp" },
                       { "poiuyjklhgADHTp" },
                       { "hgjkFfGgBnVUuKk" },
                       { "lokijuhygtfrdek" },
                       { "AaaAdDcCFfgGhHI" } };

//    char *wsk;                    // don't need this
    int i, j;
//    wsk = s1;                     // don't need this, also incompatible

    for (i = 0; i<N; i++) {
        for (j = 0; j<15; j++) {
            printf("%c", s1[i][j]);
        }
        printf("\n");
    }
    for (i = 0; i<N; i++) {
        size_t s1_len = strlen(s1[i]);
        qsort(s1[i], s1_len, 1, compare);  // improved call to qsort()
    }

    printf("\n");
    for (i = 0; i<N; i++) {
        for (j = 0; j<15; j++) {
            printf("%c", s1[i][j]);
        }
        printf("\n");
    }
    return 0;
}

int compare(const void *a1, const void *a2) {
    const unsigned char *c1 = a1;
    const unsigned char *c2 = a2;

    register int r = strcoll((const char[]){tolower(*c1), '\0'},
                             (const char[]){tolower(*c2), '\0'});
    if (r == 0) {
        if (isupper(*c1) && islower(*c2)) {
            r = -1;
        } else if (islower(*c1) && isupper(*c2)) {
            r = 1;
        }
    }

    return r;
}

节目输出:

azghtdffopAsAfp
poiuyjklhgADHTp
hgjkFfGgBnVUuKk
lokijuhygtfrdek
AaaAdDcCFfgGhHI

AAadfffghoppstz
ADgHhijkloppTuy
BFfGgghjKkknUuV
defghijkklortuy
AAaaCcDdFfGgHhI

答案 1 :(得分:1)

您是否想要对每个ROW中的所有字符进行排序,或者您想要对数组中的字符串数组(或两者)进行排序,这是非常不清楚的。两者都可以完成,但两者都有不同的compare要求。

假设您要对数组数组进行排序(如果您将它们设置为字符串则更容易),您可以期望输出如下:

$ ./bin/sortcapsfirst
azghtdffopAsAfp
poiuyjklhgADHTp
hgjkFfGgBnVUuKk
lokijuhygtfrdek
AaaAdDcCFfgGhHI

AaaAdDcCFfgGhHI
azghtdffopAsAfp
hgjkFfGgBnVUuKk
lokijuhygtfrdek
poiuyjklhgADHTp

否则,您需要先对每一行进行排序(在相同的小写字母之前对每个大写字体进行排序),然后对数组进行排序。这将导致输出如下:

$ ./bin/sortcapsfirst
azghtdffopAsAfp
poiuyjklhgADHTp
hgjkFfGgBnVUuKk
lokijuhygtfrdek
AaaAdDcCFfgGhHI

AAaaCcDdFfGgHhI
AAadfffghoppstz
ADgHhijkloppTuy
BFfGgghjKkknUuV
defghijkklortuy

你可能会让自己的事情变得更加艰难。通常,LOCALE的自然字符串排序默认情况下会先排序 Caps 。如果排序数组s1对行进行排序以便大写字母在小写之前排序,则只需要创建列数16(以便为 nul-terminated <提供空间< / em> character)然后在strcmp例程中调用compare,例如:

int compare(const void *w1, const void *w2) {

    const char *a1 = w1;
    const char *a2 = w2;

    return strcmp (a1, a2);
}

在一个示例中将它们放在一起,并在遇到 nul-terminated char时正确终止每个j循环,您可以这样做:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define N 5
#define R 16

int compare(const void *w1, const void *w2);

int main(void) {

    char s1[][R] = {{ "azghtdffopAsAfp" },
                    { "poiuyjklhgADHTp" },
                    { "hgjkFfGgBnVUuKk" },
                    { "lokijuhygtfrdek" },
                    { "AaaAdDcCFfgGhHI" }};
    int i, j;

    for (i = 0; i<N; i++) {
        for (j = 0; s1[i][j] && j<R; j++) {
            putchar(s1[i][j]);  /* don't use printf to print a single-char */
        }
        putchar('\n');
    }

    qsort (s1, N, sizeof *s1, compare);  /* sort array (rows) */

    putchar('\n');
    for (i = 0; i<N; i++) {
        for (j = 0; s1[i][j] && j<R; j++) {
            putchar(s1[i][j]);
        }
        putchar('\n');
    }
    return 0;
}

int compare(const void *w1, const void *w2) {

    const char *a1 = w1;
    const char *a2 = w2;

    return strcmp (a1, a2);
}

对于第二种情况,您在等效小写字母之前的每一行中对大写字母进行排序然后对数组进行排序,您只需添加第二个qsort比较函数并在调用之前调用它。整个阵列上qsort。例如(在相应的小写之前对每个大写字母进行排序):

int compare (const void *w1, const void *w2) {
    const char *a1 = w1;
    const char *a2 = w2;

    while (*a1 && *a2)
    {
        int r = tolower(*a1) - tolower(*a2);
        if (!r) {
            if (*a1 - *a2)
                return *a1 - *a2 > 0 ? 1 : -1;
        }
        else
            break;
        ++a1;
        ++a2;
    }
    // return *a1 - *a2; /* to sort ALLcapsfirst */
    return tolower(*a1) - tolower(*a2);
}

然后像第一个例子中那样调用qsort来对数组中的行进行排序:

int comparestr (const void *w1, const void *w2) {

    const char *a1 = w1;
    const char *a2 = w2;

    return strcmp (a1, a2);
}

将它们放在同一个示例中(使用 nul-terminated 行),您可以这样做:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define N 5
#define R 16

int compare (const void *w1, const void *w2);
int comparestr (const void *w1, const void *w2);

int main (void) {

    char s1[][R] = {{"azghtdffopAsAfp"},
                    {"poiuyjklhgADHTp"},
                    {"hgjkFfGgBnVUuKk"},
                    {"lokijuhygtfrdek"},
                    {"AaaAdDcCFfgGhHI"}};
    int i, j;

    for (i = 0; i < N; i++) {
        for (j = 0; s1[i][j] && j < R; j++)
            putchar(s1[i][j]);
        putchar('\n');
    }

    for (i = 0; i < N; i++)                     /* sort arrays */
        qsort (s1[i], R - 1, sizeof *(s1[i]), compare);
    qsort (s1, N, sizeof *s1, comparestr);      /* sort array */

    putchar('\n');
    for (i = 0; i < N; i++) {
        for (j = 0; s1[i][j] && j < R; j++)
            putchar(s1[i][j]);
        putchar('\n');
    }
    return 0;
}

int compare (const void *w1, const void *w2)
{
    const char *a1 = w1;
    const char *a2 = w2;

    while (*a1 && *a2) {
        int r = tolower (*a1) - tolower (*a2);
        if (!r) {
            if (*a1 - *a2)
                return *a1 - *a2 > 0 ? 1 : -1;
        } else
            break;
        ++a1;
        ++a2;

    }
    // return *a1 - *a2; /* to sort ALLcapsfirst */
    return tolower (*a1) - tolower (*a2);
}

int comparestr (const void *w1, const void *w2)
{
    const char *a1 = w1;
    const char *a2 = w2;

    return strcmp (a1, a2);
}

最后,如上所述,如果您要对 ALLCapsfirst 进行排序,则只需返回*a1 - *a2而不是tolower (*a1) - tolower (*a2)之间的差异。例如使用return *a1 - *a2;排序将是:

AACDFGHIaacdfgh
AAadfffghoppstz
ADHTghijkloppuy
BFGKUVfgghjkknu
defghijkklortuy

仔细看看。我完全可能误解了你的目标。如果是这样,请留言,我可以稍微帮忙。

答案 2 :(得分:0)

不要比较小写值,而是检查ASCII值。在表格中首先是大写字母,然后是小写字母: http://www.asciitable.com/

更新:如果您需要更多平台和字符集独立代码,只需添加额外的if,并使用isupper()和/或islower()检查字母大小写:

答案 3 :(得分:0)

如果您希望每个角色都能进行大写小写分心,那么您可以选择&#34; A&#34;,&#34; Aa&#34;,&#34; AB&#34;, &#34; aa&#34;,&#34; B&#34;,&#34; b&#34;,比较看起来像那样

int compare(const void *w1, const void *w2) {
    char *a1 = w1;
    char *a2 = w2;

    while (*a1 && *a2)
    {   
        register r = tolower(*a1) - tolower(*a2);
        if (r)
            return r;
        // this is the new part
        else if( isupper( *a1 ) && !isupper( *a2 ) ) {
            // w1 < w2
            return -1;
        } else if( !isupper( *a1 ) && isupper( *a2 ) ) {
            // w1 > w2
            return 1;
        }

        ++a1;
        ++a2;

    }
    return tolower(*a1) - tolower(*a2);

}

如果你想要&#34; aa&#34;在&#34; AB&#34;之前排序它可能看起来像:

int compare(const void *w1, const void *w2) {
    char *a1 = w1;
    char *a2 = w2;
    register r;
    int caseDifference = 0;

    while (*a1 && *a2)
    {   
        r = tolower(*a1) - tolower(*a2);
        if (r)
            return r;
        // this is the new part
        else if( caseDifference == 0 && ( isupper( *a1 ) && !isupper( *a2 ) ) ) {
            // w1 < w2
            caseDifference = -1;
        } else if( caseDifference == 0 && ( !isupper( *a1 ) && isupper( *a2 ) ) ) {
            // w1 > w2
            caseDifference = 1;
        }

        ++a1;
        ++a2;

    }
    r = tolower(*a1) - tolower(*a2);
    if( r != 0 )
        return r;
    else
        return caseDifference;
}

答案 4 :(得分:0)

您的比较函数不正确:它会比较多个字符而不仅仅是参数指向的字符。

如果你可以假设ASCII,这里有一个更简单的比较函数来解决这个问题:

int compare(const void *w1, const void *w2) {
    int c1 = *(const unsigned char *)w1;
    int c2 = *(const unsigned char *)w2;
    int l1 = tolower(c1);
    int l2 = tolower(c2);

    /* sort first by alphabetical character, then by case */
    return l1 != l2 ? l1 - l2 : c1 - c2;
}

另请注意,main()功能也可以简化:

#include <stdio.h>
#include <stdlib.h>

#define N 5

int compare(const void *w1, const void *w2);

int main(void) {
    char s1[N][15] = {
        { "azghtdffopAsAfp" },
        { "poiuyjklhgADHTp" },
        { "hgjkFfGgBnVUuKk" },
        { "lokijuhygtfrdek" },
        { "AaaAdDcCFfgGhHI" } };

    for (int i = 0; i < N; i++) {
        printf("%.15s\n", s1[i]);
    }
    for (int i = 0; i < N; i++) {
        qsort(s1[i], 15, sizeof(char), compare);
    }

    printf("\n");
    for (int i = 0; i < N; i++) {
        printf("%.15s\n", s1[i]);
    }
    return 0;
}