web-dev-qa-db-ja.com

文字列の配列をアルファベット順に並べ替える方法(大文字と小文字を区別する非標準の照合)

いくつかの文字列をソートするにはc言語コードが必要です。大文字と小文字を区別し、大文字と小文字が同じ文字の場合、小文字が最初に来る必要がありますにする必要があります。たとえば、次の文字列のソート結果:

eggs
bacon
cheese
Milk
spinach
potatoes
milk
spaghetti

する必要があります:

bacon
cheese
eggs
milk
Milk
potatoes
spaghetti
spinach

私はコードを書きましたが、私が得ている結果は次のとおりです。

Milk
bacon
cheese
eggs
milk
potatoes
spaghetti
spinach

私はこれを改善する方法がわからず、よく検索しました。誰もこれで私を助けることができますか?

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

int main(){
    char c;
    char name[20][10], temp[10];
    int count_name = 0;
    int name_index = 0;
    int i, j;

    while ((c = getchar()) != EOF){
        if (c == 10){
            name[count_name][name_index] = '\0';
            count_name++;
            name_index = 0;
        } else {
            name[count_name][name_index] = c;
            name_index++;
        }
    }

    for(i=0; i < count_name-1 ; i++){
        for(j=i+1; j< count_name; j++)
        {
            if(strcmp(name[i],name[j]) > 0)
            {
                strcpy(temp,name[i]);
                strcpy(name[i],name[j]);
                strcpy(name[j],temp);
            }
        }
    }

    for (i = 0; i < count_name; i++){
        printf("%s\n", name[i]);
    }
}
18
Brad Capehart

似たような言葉を一緒に...

単語のリストの場合、「大文字と小文字が異なる場合でも」「同じ」単語をグループ化する方が便利な場合があります。例えば:

_Keeping things together:          Simple "M after m":
------------------------          -------------------
mars                              mars
mars bar                          mars bar
Mars bar                          milk
milk                              milk-duds
Milk                              milky-way
milk-duds                         Mars bar
milky-way                         Milk
Milky-way                         Milky-way
_

最初の列のように単語を並べたい場合は、次の3つの方法を示します。

  • strcasecmp()strcmp()と組み合わせて使用​​します。
  • isalpha()tolower()、およびisupper()を使用して文字タイプを追跡する単一パス実装。
  • 照合テーブルを利用するシングルパス実装。

最後に、2つの選択肢について説明します。

  • 照合テーブルを使用して、任意の順序を確立します。
  • ロケールベースの照合を使用するようにロケールを設定します。

利用可能なライブラリ関数を使用する

可能な場合は、車輪の再発明を避けてください。この場合、POSIX関数strcasecmp()を使用して、大文字と小文字を区別しない比較で等しいかどうかを確認し、等しい場合はstrcmp()にフォールバックすることでこれを行うことができます。

_int alphaBetize (const char *a, const char *b) {
    int r = strcasecmp(a, b);
    if (r) return r;
    /* if equal ignoring case, use opposite of strcmp() result to get
     * lower before upper */
    return -strcmp(a, b); /* aka: return strcmp(b, a); */
}
_

(一部のシステムでは、大文字と小文字を区別しない比較関数はstricmp()または_stricmp()と呼ばれます。使用できない場合は、実装を以下に示します。)

_#ifdef I_DONT_HAVE_STRCASECMP
int strcasecmp (const char *a, const char *b) {
    while (*a && *b) {
        if (tolower(*a) != tolower(*b)) {
            break;
        }
        ++a;
        ++b;
    }
    return tolower(*a) - tolower(*b);
}
#endif
_

文字列を2回通過しないようにする

時には、既存の機能が十分に機能しない場合があり、物事を速くするために何か他のことをしなければなりません。次の関数は、strcasecmp()またはstrcmp()を使用せずに、1回のパスでほぼ同じ方法で比較を行います。ただし、アルファベット以外の文字はすべて文字よりも小さいものとして扱います。

_int alphaBetize (const char *a, const char *b) {
    int weight = 0;
    do {
        if (*a != *b) {
            if (!(isalpha(*a) && isalpha(*b))) {
                if (isalpha(*a) || isalpha(*b)) {
                    return isalpha(*a) - isalpha(*b);
                }
                return *a - *b;
            }
            if (tolower(*a) != tolower(*b)) {
                return tolower(*a) - tolower(*b);
            }
            /* treat as equal, but mark the weight if not set */
            if (weight == 0) {
                weight = isupper(*a) - isupper(*b);
            }
        }
        ++a;
        ++b;
    } while (*a && *b);
    /* if the words compared equal, use the weight as tie breaker */
    if (*a == *b) {
        return weight;
    }
    return !*b - !*a;
}
_

並べ替えにこの比較を使用すると、リストに_milk-duds_が含まれている場合でも、milkMilkが隣り合わせになります。

照合テーブルを使用する

「構成」から照合テーブルを動的に作成する方法を次に示します。文字列の比較方法を変更する対照的な手法を説明するのに役立ちます。

アルファベットの文字を、文字(またはNULバイト以外の任意の文字)の相対的な順序を記述する一種の単純なテーブルと比較する方法をマップできます。

_const char * alphaBetical =
    "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
_

この順序から、ルックアップテーブルを作成して、2つの文字がどのように互いに比較されるかを確認できます。次の関数は、テーブルがまだ最初に実行されていない場合は初期化し、それ以外の場合はテーブル検索を実行します。

_int alphaBeta_lookup (int c) {
    static int initialized;
    static char table[CHAR_MAX+1];
    if (!initialized) {
        /* leave all non-alphaBeticals in their relative order, but below
           alphaBeticals */
        int i, j;
        for (i = j = 1; i < CHAR_MAX+1; ++i) {
            if (strchr(alphaBetical, i)) continue;
            table[i] = j++;
        }
        /* now run through the alphaBeticals */
        for (i = 0; alphaBetical[i]; ++i) {
            table[(int)alphaBetical[i]] = j++;
        }
        initialized = 1;
    }
    /* return the computed ordinal of the provided character */
    if (c < 0 || c > CHAR_MAX) return c;
    return table[c];
}
_

このルックアップテーブルを使用して、alphaBetize()比較関数のループ本体を単純化できます。

_int alphaBetize (const char *a, const char *b) {
    int ax = alphaBeta_lookup(*a);
    int bx = alphaBeta_lookup(*b);
    int weight = 0;
    do {
        char al = tolower(*a);
        char bl = tolower(*b);
        if (ax != bx) {
            if (al != bl) {
                return alphaBeta_lookup(al) - alphaBeta_lookup(bl);
            }
            if (weight == 0) {
                weight = ax - bx;
            }
        }
        ax = alphaBeta_lookup(*++a);
        bx = alphaBeta_lookup(*++b);
    } while (ax && bx);
    /* if the words compared equal, use the weight as tie breaker */
    return (ax != bx) ? !bx - !ax : weight;
}
_

物事をもっと簡単にできますか?

照合テーブルを使用すると、次のような単純化された比較関数を使用して、さまざまな順序を作成できます。

_int simple_collating (const char *a, const char *b) {
    while (alphaBeta_lookup(*a) == alphaBeta_lookup(*b)) {
        if (*a == '\0') break;
        ++a, ++b;
    }
    return alphaBeta_lookup(*a) - alphaBeta_lookup(*b);
}
_

この同じ関数を使用して、alphaBetical文字列を変更することで、ほぼすべての順序(アルファベット順、逆アルファベット順、子音の前の母音など)を実現できます。ただし、類似した単語をまとめるためには、大文字の単語と小文字の単語を混在させる必要があり、大文字と小文字を無視した比較を行うことによってのみこれを行うことができます。

上記のsimple_collating()関数と指定したalphaBetical文字列では、Baconmilkの前に来るが、Marsは行くことに注意してください。 milkの後、Milkの前。

ロケールに基づいてソートする場合。

すでにロケールに定義されている照合シーケンスを使用する場合は、ロケールを設定して照合比較関数を呼び出すことができます。

_/*
 * To change the collating locale, use (for example):
       setlocale(LC_COLLATE, "en.US");
 */
int iso_collating (const char *a, const char *b) {
    return strcoll(a, b);
}
_

現在、ロケールを変更することにより、ソート順は標準化された照合シーケンスに基づいています。

21
jxh

ソート用のカスタム比較関数を作成できます。

最初に、デフォルトの strcmp ソート順を見てください:

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

const char *tgt[]={
    "bacon", "Bacon", "mIlk", "Milk", "spinach", "MILK", "milk", "eggs"
};
int tgt_size=8;

static int cmp(const void *p1, const void *p2){
    return strcmp(* (char * const *) p1, * (char * const *) p2);
}

int main(int argc, char *argv[]) {
    printf("Before sort:\n\t");
    for(int n=0; n<tgt_size; n++)
        printf("%s ", tgt[n]);

    qsort(tgt, tgt_size, sizeof(char *), cmp);

    printf("\nAfter sort:\n\t");
    for(int n=0; n<tgt_size; n++)
        printf("%s ", tgt[n]);

    return 0;
}

strcmpは、ASCII文字コードでソートします。つまり、A-Zその後a-zしたがって、すべての大文字のA〜Zは、小文字のWordの前に来ます。

Before sort:
    bacon Bacon mIlk Milk spinach MILK milk eggs 
After sort:
    Bacon MILK Milk bacon eggs mIlk milk spinach 

大文字小文字を無視するcmpで使用されるqsortで使用される独自の比較関数を書くことができます。次のようになります。

int mycmp(const char *a, const char *b) {
    const char *cp1 = a, *cp2 = b;

    for (; toupper(*cp1) == toupper(*cp2); cp1++, cp2++)
        if (*cp1 == '\0')
            return 0;
    return ((toupper(*cp1) < toupper(*cp2)) ? -1 : +1);
} 

cmpも必ず変更してください:

static int cmp(const void *p1, const void *p2){
    return mycmp(* (char * const *) p1, * (char * const *) p2);
}

バージョンを無視するケースが印刷されます:

Before sort:
    bacon Bacon mIlk Milk spinach MILK milk eggs 
After sort:
    bacon Bacon eggs Milk MILK milk mIlk spinach 

これは、POSIX関数 strcasecmp で得られる出力と同じです。

関数mycmpは、まず通常の順序で辞書式に比較します[a|A]-[z|Z]。つまり、文字の単語のようになりますが、bacon, BaconおそらくBacon, bacon。これは、qsortが 安定したソート ではなく、「Bacon」が「bacon」と等しいためです。

大文字と小文字を区別せずに比較が0の場合(つまり、「MILK」や「milk」などの同じ単語)、大文字と小文字を区別して比較し、順序を逆にすることができます。

int mycmp(const char *a, const char *b) {
    const char *cp1 = a, *cp2 = b;
    int sccmp=1;

    for (; toupper(*cp1) == toupper(*cp2); cp1++, cp2++)
        if (*cp1 == '\0')
            sccmp = 0;
    if (sccmp) return ((toupper(*cp1) < toupper(*cp2)) ? -1 : +1);

    for (; *a == *b; a++, b++)
        if (*a == '\0')
            return 0;
    return ((*a < *b) ? +1 : -1);
}

最終版の印刷:

Before sort:
    bacon Bacon mIlk Milk spinach MILK milk eggs 
After sort:
    bacon Bacon eggs milk mIlk Milk MILK spinach 

残念ながら、このアプローチはUNICODEにとって扱いにくいものになります。複雑なソートの場合、マッピングまたは 安定したソート を使用したマルチステップソートの使用を検討してください。

複雑で場所を認識するアルファベット順の照合については、 nicode照合 を検討してください。例として、異なる場所では、文字のアルファベット順が異なります。

Swedish                      z < ö
                             y == w
German                       ö < z
Danish                       Z < Å
Lithuanian                   i < y < k
Tr German                    ä == æ
Tr Spanish                   c < ch < d   
German Dictionary Sort:      of < öf
German Phonebook Sort:       öf < of

これらの区別のデフォルト値は、Unicode照合および文字列比較のデフォルトマッピングを提供するデフォルトUnicode照合要素テーブル( [〜#〜] ducet [〜#〜] )にキャプチャされます。デフォルトを変更して、辞書の並べ替えと電話帳の並べ替えの区別、異なる場所、または大文字と小文字の異なる処理をキャプチャできます。個々の場所のバリエーションは、Unicode Common Locale Data Repositoryでアクティブに追跡されます( [〜#〜] cldr [〜#〜] )。

マルチレベルの並べ替えの推奨事項は階層化されています。

Level   Description           Examples
L1      Base characters       role < roles < rule
L2      Accents               role < rôle < roles
L3      Case/Variants         role < Role < rôle
L4      Punctuation           role < “role” < Role
Ln      Identical             role < ro□le < “role”

Unicode照合の広く使用されている実装は、 ICUライブラリ にあります。いくつかの例のデフォルトのDUCET照合は次のとおりです。

b < B < bad < Bad < bäd
c < C < cote < coté < côte < côté 

ICUライブラリを探索し、 ICU Explorer で場所とターゲットを変更できます。

ギグル用に独自のバージョンのDUCETを実装したい場合は、この Pythonスクリプト で使用されている一般的な方法に従うことができます。圧倒的ではありませんが、些細なことではありません。

10
dawg

OPコードの重要な点は、関数strcmp()を使用して2つの文字列を比較することです。
したがって、この標準関数を次のように別の関数に置き換えることから始めます。

_  // We assume that the collating sequence satisfies the following rules:
  // 'A' < 'B' < 'C' < ...
  // 'a' < 'b' < 'c' < ...
  // We don't make any other assumptions.

  #include <ctype.h>      
  int my_strcmp(const char * s1, const char * s2)
  {
      const char *p1 = s1, *p2 = s2;
      while(*p1 == *p2 && *p1 != '\0' && *p2 != '\0')
          p1++, p2++;  /* keep searching... */

      if (*p1 == *p2)
         return 0;
      if (*p1 == '\0')
         return -1;
      if (*p2 == '\0')
         return +1;

      char c1 = tolower(*p1),      c2 = tolower(*p2);
      int  u1 = isupper(*p1) != 0, u2 = isupper(*p2) != 0;
      if (c1 != c2)
        return c1 - c2;  // <<--- Alphabetical order assumption is used here 
      if (c1 == c2)
        return u1 - u2;
  }
_

最後の行はこの方法で圧縮できます:

_     return (c1 != c2)? c1 - c2: u1 - u2;
_

これで、strcmp()my_strcmp()に置き換えることで、目的の結果が得られます。

ソートアルゴリズムでは、次の3つの主要な側面を別々に考えることをお勧めします。

  • 比較関数。
  • 使用する抽象ソートアルゴリズム。
  • 2つのアイテムを交換する必要がある場合、そのデータの方法は配列内で「移動」されます。

これらの側面は個別に最適化できます。
したがって、例として、比較関数を適切に設定したら、次の最適化ステップは、double forソートアルゴリズムを置き換えることです。 quicksortのような、より効率的な方法によって。
特に、標準ライブラリ_<stdlib.h>_の関数qsort()はそのようなアルゴリズムを提供するので、プログラミングを気にする必要はありません。
最後に、配列情報を保存するために使用する戦略は、パフォーマンスに影響を与える可能性があります。
「charの配列の配列」ではなく、「charの配列の配列」などの文字列を保存する方が効率的です。これは、2つのchar配列全体を交換するよりもポインタを交換する方が速いためです。

ポインターの配列

追加注記:最初の3つのif()は実際には冗長です。なぜなら、次の文の論理は、 _*p1_または_*p2_は0です。ただし、これらのif()を保持することにより、コードが読みやすくなります。

3
pablo1977

ここで、私がそれを正しく理解したなら、あなたは私が次のように説明するようなものが欲しいです:

大文字小文字を区別しないソート。同点の場合、タイブレーク条件「小文字が最初に来る」が使用されます。

それは次のようなものです:

  1. earlier_letter_in_the_alphabet < later_letter_in_the_alphabetケースを無視

  2. lowercase < uppercase

  3. shorter_Word < wider_Word
    • これは言及されていませんでしたが、辞書にある辞書編集順序から借りました
    • 比較で'\0'を可能な限り低くするだけで利用できます

ステップ2は、1が何も区別しなかった場合にのみ実行されます。ステップ3はすでに1でチェックされます。これらはすべて文字ごとに行われます。つまり、文字列全体がタイになったときだけでなく、対応する文字がタイになったらすぐに2に切り替える必要があります。


これが正しかったと仮定すると、今必要なのは、任意の2つの文字列に対してこの比較を行う関数を作成することだけです。

#include <ctype.h>  // for tolower and islower

int my_character_compare(const char a, const char b)
{
    int my_result;

    my_result = tolower(a) - tolower(b);
    // unless it is zero, my_result is definitely the result here
    // Note: if any one of them was 0, result will also properly favour that one


    if (my_result == 0 && a != b)
    // if (could not be distinguished with #1, but are different)
    {
        // means that they are case-insensitively same
        // but different...
        // means that one of them are lowercase, the other one is upper
        if (islower(a))
            return -1;  // favour a
        else
            return 1;   // favour b
    }


    // regardless if zero or not, my_result is definitely just the result
    return my_result;
}

int my_string_compare(const char * a, const char * b)
{
    int my_result;

    my_result = my_character_compare(*a, *b);
    // unless it is zero, my_result is definitely the result here

    while (my_result == 0 && *a != 0)
    // current characters deemed to be same
    // if they are not both just 0 we will have to check the next ones
    {
        my_result = my_character_compare(*++a, *++b);
    }

    // whatever the my_result has been:
    //   whether it became != zero on the way and broke out of the loop
    //   or it is still zero, but we have also reached the end of the road/strings
    return my_result;
}

比較関数は、慣例/規則により、最初のパラメーターを優先する場合は負の値、2番目のパラメーターを優先する場合は負の値、それらを区別できない場合はゼロを返す必要があります。 strcmpを使用する方法で既に知っている可能性が高い追加情報です。

以上です!コードのstrcmpをここのmy_string_compareに置き換えて、これらの定義を上に置くと、正しい結果が得られます。実際、問題の入力例に期待される結果を提供します。

もちろん定義を短くすることもできますが、何が起こっているのかを理解しやすくするために定義を長くしました。たとえば、次のようにすべてを煮詰めることができます。

#include <ctype.h>

int my_string_compare(const char * a, const char * b)
{
    int my_result;

    while (*a || *b)
    {
        if ((my_result = tolower(*a) - tolower(*b)))
            return my_result;
        if (*a != *b)
            return (islower(*a)) ? -1 : 1;
        a++;
        b++;
    }

    return 0;
}

本質的に他の1つと同じ、あなたが好きなものを使用できます。またはさらに良い、1つを書きます。

3
ThoAppelsin

私はこの議論に遅れており、白鳥に乗って素晴らしい賞を受賞することを特に期待していませんが、私が最初に見ていたイディオムを使用した解決策を見ていませんでした。

問題の仕様を読む際に最初に考えたのは、基本的に@jxhの照合テーブルの使用の概念にあるカスタム照合シーケンスの形式です。大文字と小文字を区別しないことは中核的な概念ではなく、単なる奇妙な順序だと思います。

そこで、純粋に代替実装として次のコードを提供します。これはglibcに固有です-qsort_r(3)が使用されます-しかし、より軽いアプローチのように感じ、実行時に多くの照合シーケンスをサポートします。しかし、簡単にテストされており、さまざまな弱点を見逃している可能性が非常に高いです。その中でも、私はユニコードやワイド文字の世界全般に特に注意を払っていません。また、負の配列添え字を避けるためのunsigned charへのキャストは疑わしいと感じています。

#include <stdio.h>
#include <limits.h>

/*
 * Initialize an index array associated with the collating
 * sequence in co. The affected array can subsequently be
 * passed in as the final client data pointer into qsort_r
 * to be used by collating_compare below.
 */
int
collating_init(const char *co, int *cv, size_t n)
{
    const unsigned char *uco = (const unsigned char *) co;
    const unsigned char *s;
    size_t i;

    if (n <= UCHAR_MAX) {
        return -1;
    }
    for (i = 0; i < n; i++) {
        /* default for chars not named in the sequence */
        cv[i] = UCHAR_MAX;
    }
    for (s = uco; *s; s++) {
        /*
         * the "collating value" for a character's own
         * character code is its ordinal (starting from
         * zero) in the collating sequence. I.e., we
         * compare the values of cv['a'] and cv['A'] -
         * rather than 'a' and 'A' - to determine order.
         */
        cv[*s] = (s - uco);
    }

    return 0;
}

static int
_collating_compare(const char *str1, const char *str2, int *ip)
{
    const unsigned char *s1 = (const unsigned char *) str1;
    const unsigned char *s2 = (const unsigned char *) str2;

    while (*s1 != '\0' && *s2 != '\0') {
        int cv1 = ip[*s1++];
        int cv2 = ip[*s2++];

        if (cv1 < cv2) return -1;
        if (cv1 > cv2) return 1;
    }

    if (*s1 == '\0' && *s2 == '\0') {
        return 0;
    } else {
        return *s1 == '\0' ? -1 : 1;
    }
}

int
collating_compare(const void *v1, const void *v2, void *p)
{
    return _collating_compare(*(const char **) v1,
                              *(const char **) v2,
                              (int *) p);
}

前のコードは、別のモジュールまたはライブラリに配置できるコードに近いですが、独自のヘッダーファイル(またはヘッダーファイルのエントリ)がありません。私自身のテストでは、単に上記のコードと以下のコードをcustom_collat​​e_sort.cという名前のファイルに連結し、

gcc -DMAIN_TEST -Wall -o custom_collate_sort custom_collate_sort.c

...それをコンパイルします。

#if defined(MAIN_TEST)

/* qsort_r is a GNU-ey thing... */

#define __USE_GNU
#include <stdlib.h>
#include <string.h>

#define NELEM(x) (sizeof x / sizeof 0[x])

static int
cmp(const void *v1, const void *v2)
{
    return strcmp(*(const char **) v1, *(const char **) v2);
}

static int
casecmp(const void *v1, const void *v2)
{
    return strcasecmp(*(const char **) v1, *(const char **) v2);
}

int
main(int ac, char *av[])
{
    size_t i;

    int cval[256], ret;
    int cval_rev[256], rret;

    char *tosort[] = {
        "cheeSE", "eggs", "Milk", "potatoes", "cheese", "spaghetti",
        "eggs", "milk", "spinach", "bacon", "Egg", "Apple", "PEAR",
        "pea", "berry"
    };

    ret = collating_init("aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVxXyYzZ",
                         cval, NELEM(cval));

    rret = collating_init("ZzYyXxVvUuTtSsRrQqPpOoNnMmLlKkJjIiHhGgFfEeDdCcBbAa",
                          cval_rev, NELEM(cval_rev));

    if (ret == -1 || rret == -1) {
        fputs("collating value array must accomodate an index of UCHAR_MAX\n", stderr);
        return 1;
    }

    puts("Unsorted:");
    for (i = 0; i < NELEM(tosort); i++) {
        printf("  %s\n", tosort[i]);
    }

    qsort((void *) tosort, NELEM(tosort), sizeof tosort[0], cmp);

    puts("Sorted w/ strcmp:");
    for (i = 0; i < NELEM(tosort); i++) {
        printf("  %s\n", tosort[i]);
    }

    qsort((void *) tosort, NELEM(tosort), sizeof tosort[0], casecmp);

    puts("Sorted w/ strcasecmp:");
    for (i = 0; i < NELEM(tosort); i++) {
        printf("  %s\n", tosort[i]);
    }

    qsort_r((void *) tosort, NELEM(tosort), sizeof tosort[0],
            collating_compare, (void *) cval);

    puts("Sorted w/ collating sequence:");
    for (i = 0; i < NELEM(tosort); i++) {
        printf("  %s\n", tosort[i]);
    }

    qsort_r((void *) tosort, NELEM(tosort), sizeof tosort[0],
            collating_compare, (void *) cval_rev);

    puts("Sorted w/ reversed collating sequence:");
    for (i = 0; i < NELEM(tosort); i++) {
        printf("  %s\n", tosort[i]);
    }

    return 0;
}

#endif /* MAIN_TEST */
3
sjnarv

プログラムに必要な標準ヘッダーファイル:

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

メインプログラムはここから始まります。

//Global Variables Required
//=========
const char *tgt[]={"bacon", "Bacon", "mIlk", 
        "Milk", "spinach", "MILK", "milk", "eggs"};    //Array for sorting
int tgt_size=8;                                        //Total Number of Records
int     SortLookupTable[128];                          //Custom sorting table
typedef int      cmp_t (const void *, const void *);




int main(int argc, char *argv[]) {
    printf("Before sort:\n\n");
    int n=0;
    for(n=0; n<tgt_size; n++)
        printf("%s\n", tgt[n]);

    CreateSortTable();

    myQsort(tgt, tgt_size, sizeof(char *), &compare);
    printf("\n\n====\n\n");
    for(n=0; n<tgt_size; n++)
        printf("%s\n", tgt[n]);

    return 0;
}

必要に応じてカスタムソートテーブル:

void CreateSortTable(void){
    int     i;
    for (i = 0; i < 128; i++){
        SortLookupTable[i] = 0;
    }
    char *s;
    s=(char *)malloc(64);
    memset(s, 0, 64);
    strcpy(s, "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ");

    i=1;
    for (; *s; s++){
        SortLookupTable[(int) ((unsigned char) *s)]=i;
        i++;
    }
}

クイックソートアルゴリズム、提供されている標準ライブラリも使用できます。

//Some important Definations required by Quick Sort
=======
#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
    es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;

#define swap(a, b)                          \
    if (swaptype == 0) {                    \
        long t = *(long *)(a);              \
        *(long *)(a) = *(long *)(b);        \
        *(long *)(b) = t;                   \
    } else                                  \
        swapfunc(a, b, es, swaptype)

#define vecswap(a, b, n)    if ((n) > 0) swapfunc(a, b, n, swaptype)


#define swapcode(TYPE, parmi, parmj, n) {       \
    long i = (n) / sizeof (TYPE);               \
    register TYPE *pi = (TYPE *) (parmi);       \
    register TYPE *pj = (TYPE *) (parmj);       \
    do {                                        \
        register TYPE   t = *pi;                \
        *pi++ = *pj;                            \
        *pj++ = t;                              \
        } while (--i > 0);                      \
}

#define min(a, b)   (a) < (b) ? a : b


//Other important function
void swapfunc(char *a, char *b, int n, int swaptype){
    if(swaptype <= 1)
        swapcode(long, a, b, n)
    else
        swapcode(char, a, b, n)
}

char * med3(char *a, char *b, char *c, cmp_t *cmp){
    if ( cmp(a, b) < 0){
        if (cmp(b, c) < 0){
            return b;
        }else{
            if ( cmp(a, c) < 0){

                return c;
            }else{
                return a;
            }
        }
    }else{
        if (cmp(b, c) < 0){
            return b;
        }else{
            if ( cmp(a, c) < 0){
                return a;
            }else{
                return c;
            }
        }
    }
}

//Custom Quick Sort

void myQsort(void *a, unsigned int n, unsigned int es, cmp_t *cmp){
    char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
    int d, r, swaptype, swap_cnt;

loop:   SWAPINIT(a, es);
    swap_cnt = 0;
    if (n < 7) {
        for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
            for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es){
                swap(pl, pl - es);
            }
        return;
    }
    pm = (char *)a + (n / 2) * es;
    if (n > 7) {
        pl = a;
        pn = (char *)a + (n - 1) * es;
        if (n > 40) {
            d = (n / 8) * es;
            pl = med3(pl, pl + d, pl + 2 * d, cmp);
            pm = med3(pm - d, pm, pm + d, cmp);
            pn = med3(pn - 2 * d, pn - d, pn, cmp);
        }
        pm = med3(pl, pm, pn, cmp);
    }
    swap(a, pm);
    pa = pb = (char *)a + es;

    pc = pd = (char *)a + (n - 1) * es;
    for (;;) {
        while (pb <= pc && (r = cmp(pb, a)) <= 0) {
            if (r == 0) {
                swap_cnt = 1;
                swap(pa, pb);
                pa += es;
            }
            pb += es;
        }
        while (pb <= pc && (r = cmp(pc, a)) >= 0) {
            if (r == 0) {
                swap_cnt = 1;
                swap(pc, pd);
                pd -= es;
            }
            pc -= es;
        }
        if (pb > pc)
            break;
        swap(pb, pc);
        swap_cnt = 1;
        pb += es;
        pc -= es;
    }
    if (swap_cnt == 0) {  /* Switch to insertion sort */
        for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
            for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0;
                 pl -= es)
                swap(pl, pl - es);
        return;
    }

    pn = (char *)a + n * es;
    r = min(pa - (char *)a, pb - pa);
    vecswap(a, pb - r, r);
    r = min(pd - pc, pn - pd - es);
    vecswap(pb, pn - r, r);
    if ((r = pb - pa) > es)
        myQsort(a, r / es, es, cmp);
    if ((r = pd - pc) > es) {
        /* Iterate rather than recurse to save stack space */
        a = pn - r;
        n = r / es;
        goto loop;
    }
}

最も重要な機能は次の2つです。

unsigned char Change(char a){
    return (unsigned char ) SortLookupTable[(int)a];
}


int compare (const void *a, const void *b){
    char *s1= *(char **)a;
    char *s2= *(char **)b;
    int ret, len, i;
    ret=0;

    if (strlen((void*)s1) > strlen((void*)s2)){
        len=strlen((void*)s1);
    }else{
        len=strlen((void*)s2) ;
    }

    for(i=0; i<len; i++){
        if ( s1[i] != s2[i]){
            if ( Change(s1[i]) < Change(s2[i]) ){
                ret=0;
                break;
            }else{
                ret=1;
                break;
            }
        }
    }

    return ret;
}
1
Vineet1982