2017-05-01 9 views
0

下のコードでは、0番目の列(int型のsku)で最初にソートできるようにする5x4配列があり、等号がある場合は1番目の列(植物、文字列:例:CA72> CA70 CAは常に2つの整数、この場合は7と2の前にあります)、最後に2番目の列(double型の時代)になります。Cで各列の型が異なる2次元配列をソートする方法は?

残念ながら、この現在のコードでは、データを構造体に正しく解析することさえできません。私は構造体を使用したことがありませんので、私の質問は二重です:構造体の初期化でどこが間違っているのですか?最後に、上記のように一度に1つの列を並べ替える方法はありますか?

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

struct st_ex { 
    int sku; 
    char plant[4]; 
    char age_code[10]; 
    int qty; 
    // char product[16]; 
    // float price; 
}; 

int comp_sku(const void *a, const void *b) { //qsort struct comparision function (sku int field) 
    struct st_ex *ia = (struct st_ex *)a; 
    struct st_ex *ib = (struct st_ex *)b; 
    return (int)(ia->sku - ib->sku); 
    /* int comparison: returns negative if b > a 
    and positive if a > b. */ 
} 

int comp_plant(const void *a, const void *b) { //qsort struct comparison function (plant C-string field) 
    struct st_ex *ia = (struct st_ex *)a; 
    struct st_ex *ib = (struct st_ex *)b; 
    return strcmp(ia->plant, ib->plant); 
    /* strcmp functions works exactly as expected from 
    comparison function */ 
} 

int comp_age(const void *a, const void *b) { //qsort struct comparison function (age double field) 
    struct st_ex *ia = (struct st_ex *)a; 
    struct st_ex *ib = (struct st_ex *)b; 
    return strcmp(ia->age_code, ib->age_code); 
} 


void print_array(struct st_ex *array, size_t len){ 
    size_t i; 
    for(i=0; i<len; i++) 
     printf("[ SKU: %i \t Plant: %s \t Code: %s \t Qty: %i ]\n", array[i].sku, array[i].plant, array[i].age_code, array[i].qty); 
    puts("--"); 
} 

void sort_struct(void) { 
    struct st_ex structs[] = {{"4010","CA50","15834.0261","5"}, 
           {"4010","CA50","15876.0261","6"}, 
           {"4010","CA52","14834.0261","7"}, 
           {"4200","CA72","16028.3085","9"}, 
           {"4001","CA72","15022.0001","9"}}; 

    size_t s_len = sizeof(structs)/sizeof(struct st_ex); 

    puts("*** Original array..."); 
    print_array(structs, s_len); //Print original array 

    puts("*** Sorted by sku..."); 
    qsort(structs, s_len, sizeof(struct st_ex), comp_sku); 
    print_array(structs, s_len); //Print array sorted by sku 

    puts("*** Sorted by plant..."); 
    qsort(structs, s_len, sizeof(struct st_ex), comp_plant); 
    print_array(structs, s_len); //Print array sorted by plant 

    /** How do I sort by sku first and then plant? (provided the sku's are equal)*/ 
} 

void main() { 
    sort_struct(); 
} 

ご協力いただきありがとうございます。

注:ここで私は今、変化してきたものだが、それはまだダブルスをソートすることができません。

int comp_sku(const void *a, const void *b) { //qsort struct comparision function (sku int field) 
    struct st_ex *ia = (struct st_ex *)a; 
    struct st_ex *ib = (struct st_ex *)b; 
    if (ia->sku == ib->sku && ia->age_code == ib->age_code){ 
     return strcmp(ia->age_code, ib->age_code); 
    } 
    else if(ia->sku == ib->sku){ 
     return strcmp(ia->plant, ib->plant); 
    } 
    else{return (ia->sku-ib->sku);} 
} 

フォローアップ編集:だから

私が望んでいた場合に、例えば[4001、CA87,22345.234、48] [...] x 100,000のような行列を読み取る.csvファイルを使用してこのデータセットをインポートします。私は新しいニーズに合わせてコードを変更しようとしましたが、サイズを定義することなく構造体の新しい命名が問題になっています。私は間違って何をしていますか?

void main() { 
    char   buffer[1024], *line; 
    struct st_ex input; 
    unsigned long lineno = 0UL; 
    FILE* inputdoc = fopen("input/old_input.csv", "r"); 

    while (1) { 
     line = fgets(buffer, sizeof buffer, inputdoc); 
      if (!line) 
      break; 

     lineno++; 

      if (sscanf(line, "%d, %4s, %10s, %d", &input.sku, &input.plant, &input.age, &input.qty) != 4) { 
      fprintf(stderr,"Cannot parse line %lu.\n", lineno); 
      break; 
     } 

     /* Do something with 'struct st_ex input'. */ 

    }; 

    size_t s_len = sizeof(input)/sizeof(struct st_ex); 
    puts("*** Original array...\n"); 
    //print_array(input, s_len); //Print original array 

    puts("\n*** Sorted by sku, plant, age.\n\n"); 
    qsort(input, s_len, sizeof(struct st_ex), comp_sku_plant_age); 
    print_array(input, s_len); //Print array sorted by sku 
} 
+2

1) 'チャー植物[4]。 char age_code [10]; ' - >' char plant [5]; char age_code [11]; 2) '{" 4010 "、" CA50 "、" 15834.0261 "、" 5 "}' - > '{4010、" CA50 "、" 15834.0261 "、5}' – BLUEPIXY

+1

で並べ替えSKUは、2つの一致が得られた場合、compare関数から0を返す代わりに、戻り値を計算する文字列を調べます。つまり、 'comp_sku'関数を修正します。 'if(ia-> sku == ib-> sku)はstrcmp(ia-> plant、ib-> plant)を返します。 else return(ia-> sku-ib-> sku); ' – enhzflep

+1

あなたの用語は混乱しているか混乱しています。 2D配列ではなく、構造体型の配列(たぶん1次元)を記述しているようです。それ以外はすべて、Cのすべての配列は均質です。 1つのタイプの要素のみを含みます。複数のタイプではありません。 (ポインタや物事でゲームをすることもできますので、配列に異なる型を格納するように見えますが、実際にはそうではありません - あなたが扱っているものよりも複雑です)。 '異なるメンバーの異なる基準を持つ構造タイプの配列をソートするにはどうすればよいですか?' –

答えて

1

いいえ、あなたがしたい場合

struct st_ex { 
    int sku; 
    char plant[5]; /* 4 characters + string terminating '\0' at end */ 
    double age; 
}; 

のようなものplantフィールドで絆を壊し、そしてageフィールドであり絆を壊し、skuフィールドでソートするために、使用

int compare_st_ex(const void *ptr1, const void *ptr2) 
{ 
    const struct st_ex *const ref1 = (const struct st_ex *)ptr1; 
    const struct st_ex *const ref2 = (const struct st_ex *)ptr2; 
    int      plant; 

    /* Compare 'sku' fields. */ 
    if (ref1->sku < ref2->sku) 
     return -1; 
    else 
    if (ref1->sku > ref2->sku) 
     return +1; 

    /* Compare 'plant' fields. */ 
    plant = strncmp(ref1->plant, ref2->plant, sizeof ref1->plant); 
    if (plant) 
     return plant; 

    /* Compare 'age' fields. */ 
    if (ref1->age < ref2->age) 
     return -1; 
    else 
    if (ref1->age > ref2->age) 
     return +1; 

    /* The two are essentially equal. */ 
    return 0; 
} 

上記では、私はif (foo != 0)の略語としてif (foo)を使用しています。

フィールドが異なる場合は、各フィールドの比較のみが返される方法を参照してください。フィールドが等しいと比較すると、次のフィールドに移動します。

1行に1レコードのファイルがある場合は、ループを使用して行を読み取ってから、フィールドを構造メンバーに解析します。 sscanf()

char   buffer[1024], *line; 
    struct st_ex one; 
    unsigned long lineno = 0UL; 

    while (1) { 

     line = fgets(buffer, sizeof buffer, stdin); 
     if (!line) 
      break; 

     lineno++; 

     if (sscanf(line, "%d %4s %lf", &one.sku, one.plant, &one.age) != 3) { 
      fprintf(stderr,"Cannot parse line %lu.\n", lineno); 
      break; 
     } 

     /* Do something with 'struct st_ex one'. */ 

    }; 

%dスキャンパターンがintを変換します。 %4sは、最大4文字の空白文字のない文字列または文字列を変換します(文字列終端文字列を含めるには、少なくとも5文字の空き領域が必要です。\0)。 %lfdoubleを変換します。戻り値は成功したコンバージョン数です。

plantフィールドで処理できる文字列の長さを指定し、文字列に最後にヌルバイト('\0')が必要であることを覚えておくことが重要です。従って、パターンで供給される数は、通常、割り当てられたサイズよりも1少ない。長さを指定しておらず、そこに長い文字列があると、関数のscanfファミリはフィールド外のメモリをうっかり上書きし、クラッシュや奇妙なエラーにつながります。あなたの講師がそれは問題ではないと言っても、それは今までにやってはいけません。 (もしそうであれば、彼らは間違っていて、彼らは自分自身を恥ずかしく思うべきです。ちょうど不満足な問題につながり、理解して避けるのは簡単です。)

最後に、構造型変数あたかもそれが通常の基本変数型であるかのように。つまり、struct st_ex foo[5];があれば、それは完全に大丈夫です。上記のスニペットのfoo[0] = one;

+0

ありがとうございます!私はあなたが最後の段落まであなたが言ったことすべてを理解していると信じています。その方法を使って.csvファイルを解析できるということですか? (私は主な質問の編集でその性質の何かを試しました)。 –

+0

@MatthewR:最後の段落は、フィールドを個別に割り当てる必要がないことを思い出させるだけです。構造全体を一度に割り当てることができます。 CSVの場合は、やや異なる方法をお勧めします。 'ftgets()'はCSVフィールドを読み込み、引用されたフィールド(埋め込み改行とコンマを含む)とエスケープされた引用符を適切に扱いますが、 'fgets()'を使って行を読むのではなく、次のレコードには渡されません。 'fnext()'は、現在のレコード内のフィールドをスキップし、次のレコードの先頭に移動します。 –

0

は(コメントで述べたように)文字列の初期化とは対照的に、文字列の長さとintデータ型に関する問題のほかに、以下の比較関数は、あなたの問題を解決する必要があります。アイデアは限りが比較として、他の比較関数への呼び出しのカスケードを設定することです等しい:あなたは構造体の配列を持っている

int comp_sku_plant_age(const void *a, const void *b) { 
    int result; 
    if (!(result=comp_sku(a, b))) { 
     if (!(result=comp_plant(a, b))) { 
      result=comp_age(a, b); 
     } 
    } 
    return result; 
} 
関連する問題