C言語関係掲示板

過去ログ

No.503.大量のファイルの中身のソート

[戻る] [ホームページ]
No.3813

ファイルの中身のソート
投稿者---たかし(2002/12/11 23:28:11)


ファイルの中身をソートして昇順に並び替えもとの
ファイルに出力したいのですが、どうすればよいの
ですか。

以下のようなファイルのデータを
BNG01,5
BR03,800
BR01,450
BR02,450
BNG02,100
BNG01,10
BR01,15
BNG02,800
BNG03,100
BR02,100
BNG02,250

以下のようにソートし昇順でファイルに出力
BNG01,10
BNG01,5
BNG02,100
BNG02,250
BNG02,800
BNG03,100
BR01,15
BR01,400
BR02,450
BR03,800

お願いします。


No.3828

Re:ファイルの中身のソート
投稿者---ともじ(2002/12/12 20:49:37)


こんばんは。

>ファイルの中身をソートして昇順に並び替えもとの
>ファイルに出力したいのですが、どうすればよいの
>ですか。

データの件数はどれくらいですか。
件数のMAXが決まっていて、それほど多くない場合には、
一旦、自動変数で確保した構造体の配列に読み込んでソート処理を
行い、元のファイルに再書き込みすればよいと思いますが、
データ量のMAXが不明で相当多い場合には、動的に読み込むエリアを
確保する必要があります。

構造体を複数のキーでソートするプログラムは、こちらが
参考になるのではないでしょうか。
http://f1.aaa.livedoor.jp/~pointc/log241.html

No.3848

qsortについて
投稿者---たかし(2002/12/13 17:50:57)


回答ありがとうございます。

http://f1.aaa.livedoor.jp/~pointc/log241.html
の内容をみたのですが、qsortの個所のコードについて教えてください。
qsortの第4パラメータに比較を実行する関数を指定していますが、
呼ばれる側のパラメータの変数がポインタになっています。
何故、このようになるのですか。また、そのアドレスを構造体のポインタ
に渡しています。どうしてなのですか。

int comp(const void *p1, const void *p2)
{
const DateTbl *a = p1, *b = p2;

int comp(const void *p1, const void *p2)
         ↓
int comp(char *p1, char *p2)
としては、いけないのですか。?

比較処理の関数で、
return diff ? diff : strcmp(a->nensuu, b->nensuu);
return diff ? diff : a->nensuu - b->nensuu;
で、「diff ?」、「diff ? diff : 」の動きについて教えてください。

教えて、教えてばかりですみませんが、見たままをそのまま利用しても、
理解していないまま使用しているので身につきません。
お願いします。


No.3854

Re:qsortについて
投稿者---ともじ(2002/12/13 21:35:30)


こんばんは。

>qsortの第4パラメータに比較を実行する関数を指定していますが、
>呼ばれる側のパラメータの変数がポインタになっています。
>何故、このようになるのですか。

値渡しにしても元のデータを入れ換えることはできませんね。
アドレスを渡して始めて、元のデータを入れ換えることができます。

>int comp(const void *p1, const void *p2)
>{
> const DateTbl *a = p1, *b = p2;
>
>int comp(const void *p1, const void *p2)
>         ↓
>int comp(char *p1, char *p2)
>としては、いけないのですか。?

voidポインタはどの型のポインタとも互換があります。ですから、
どの型のポインタでも使えるようにqsortの第4引数の比較関数は
voidポインタを引数にします。
そして、No241では受け取ったポインタを改めて
 const DateTbl *a = p1, *b = p2;
と本来の構造体のポインタに代入しています。
仮に、
 int comp(const DateTbl *a, const DateTbl *b)
とした場合には、通常コンパイルで警告が出ます。
交換するデータは構造体ですから、
 int comp(char *p1, char *p2)
では、警告ではなくエラーになります。

>比較処理の関数で、
>return diff ? diff : strcmp(a->nensuu, b->nensuu);
>return diff ? diff : a->nensuu - b->nensuu;
>で、「diff ?」、「diff ? diff : 」の動きについて教えてください。

条件式 ? 式1 : 式2 は条件演算子で、
http://www9.plala.or.jp/sgwr-t/c/sec14.html#s14-3
条件式が「真」なら「式1」を、「偽」なら「式2」を値とします。
ifで
 return diff ? diff : strcmp(a->nensuu, b->nensuu);
を書き換えると、
    if (diff != 0) 
	return diff;
    else
	return strcmp(a->nensuu, b->nensuu);

になります。つまり、ageが等しければ、nensuuの比較結果を返却します。

>教えて、教えてばかりですみませんが、見たままをそのまま利用しても、
>理解していないまま使用しているので身につきません。
>お願いします。

その通りです。
この過去ログを参照するように薦めたのは、とても洗練されたプログラム
だからです。その分、理解するのは大変かもしれません。

ところで、こちらは私が書いたもので、かなり平易です。
元のプログラムがバブルソートを使っているので、そのままバブルソート
を使っています。効率はよくありませんが、わかりやすいのではないで
しょうか。
http://www2.realint.com/cgi-bin/tarticles.cgi?pointc+3761


No.3865

ありがとうございます。
投稿者---たかし(2002/12/14 10:46:19)


ともじさんありがとうございます。
親切に説明していただきありがとうございます。
大変勉強になります。


No.3907

ファイルの中身のソート
投稿者---たかし(2002/12/16 23:32:06)


データの件数は決まっていません。MAXも不明です。
読み込むデータ量がわからないので、自動変数や、動的にエリアを確保しても、オーバーフローが発生しました。
読み込む量がわからないので、構造体確保してソートするのはちょっと無理でした。
別の方法を考えてやってみます。



No.3909

Re:ファイルの中身のソート
投稿者---かずま(2002/12/17 01:21:52)


> データの件数は決まっていません。MAXも不明です。
> データの件数は決まっていません。MAXも不明です。
> 読み込むデータ量がわからないので、自動変数や、動的にエリアを確保しても、
> オーバーフローが発生しました。

オーバーフローが発生したときのデータ量は何メガバイトでしたか。
コンパイラは何ですか。LSI C-86 ?
OS は何ですか。MS-DOS ?

> 読み込む量がわからないので、構造体確保してソートするのはちょっと無理でした。

なぜ、構造体がいるんでしょうか。単に、行をソートするだけでいけないのですか。

> 別の方法を考えてやってみます。

例えば、こんなプログラムではいかがでしょうか。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int compare(const void *a, const void *b)
{
    return strcmp(*(char **)a, *(char **)b);
}

int main(int argc, char **argv)
{
    FILE *fp;
    char buf[1024], **line = NULL;
    int size = 1024, n, i;

    if (argc != 2) return printf("usage: %s file\n", argv[0]), 1;
    fp = fopen(argv[1], "r+");
    if (fp == NULL) return printf("can't open %s\n", argv[1]), 1;

    for (n = 0; fgets(buf, sizeof buf, fp); n++) {
        if (line == NULL || n >= size) {
            line = realloc(line, size *= 2);
            if (line == NULL) return puts("out of memory"), 1;
        }
        line[n] = strdup(buf);
        if (line[n] == NULL) return puts("out of memory"), 1;
    }
    qsort(line, n, sizeof *line, compare);
    rewind(fp);
    for (i = 0; i < n; i++)
        fputs(line[i], fp);
    fclose(fp);
    return 0;
}


No.3911

Re:ファイルの中身のソート
投稿者---かずま(2002/12/17 01:40:15)


>    line = realloc(line, size *= 2);

    line = realloc(line, (size *= 2) * sizeof(char *));

に訂正します。


No.3945

ソースの中の処理について
投稿者---said(2002/12/18 13:00:01)


かずまさんが載せられるソースには感心します。
省略できるところは、省略して、ステップ数とかも減らしてあります。

しかし、ちょっと難しいところがあって理解に苦しみます。

> line = realloc(line, size *= 2);
line = realloc(line, (size *= 2) * sizeof(char *));
に訂正します。

*(char **)a, *(char **)b

の処理につてい教えください。


No.3954

Re:ソースの中の処理について
投稿者---かずま(2002/12/19 11:23:40)


> line = realloc(line, (size *= 2) * sizeof(char *));
> に訂正します。
> *(char **)a, *(char **)b
> の処理につてい教えください。
    size *= 2;
    line = realloc(line, size * sizeof(char *));
って書けばよかったのかな。sizeof(char *) は sizeof *line でもよい。
最初は、size=1024, line=NULL ですから、size *= 2; で、size=2048 に
なり、line = realloc(NULL, 2048 * sizeof(char *)); で、line は、
2048個の char * を要素に持つ配列の先頭要素を指すことになります。

次は、line = realloc(line, 4096 * sizeof(char *)); で、line は、
4096個の char * を要素に持つ配列の先頭要素を指すことになります。
元の配列の 2048個のデータは、新しい配列にコピーされ、元の配列の
領域は free されます。
int compare(const void *a, const void *b)
    { return strcmp(*(char **)a, *(char **)b); }
のほうですが、a と b は、line[x] と line[y] のアドレスです。
すなわち、a = &line[x], b = &line[y] ですね。

strcmp(line[x], line[y]) を実行したいのですが、strcmp(a, b) だと、
strcmp(&line[x], &line[y]) になってしまいます。

strcmp(*a, *b) としたいところですが、a は void * なので、何を指して
いるのか分からず、char * を指していることをキャストで明示しないと
いけません。(char **)a とキャストすることにより、それは line[x] への
ポインタになりますから、*(char **)a で、line[x] の値になります。

No.3958

Re:ソースの中の処理について
投稿者---said(2002/12/19 14:22:17)


勉強になります。

なぜ、領域を確保するのに、「 (size *= 2) * sizeof(char *)」として
いるのかわかりませんでした。
2048とか4096としただけでば、配列の要素を確保しているに過ぎず、
「char *」をくわえることにより、char*の配列要素を確保したこと
になるのですね。
line = realloc(line, size *= 2) ;

line = realloc(line, (size *= 2) * sizeof(char *)) ;
との違いとはそういうことですか。

line=realloc(line, size *= 2);
としたら、2047でMemory fault(coredump)となり落ちてしまいました。


No.3925

Re:ファイルの中身のソート
投稿者---ともじ(2002/12/17 18:21:34)


>なぜ、構造体がいるんでしょうか。単に、行をソートするだけでいけないのですか。

これは、私が最初に構造体を使うようにアドバイスしたのが
いけなかったようです。

 BNG01,5
 BNG01,10
と並べなければいけないのだと思い込んでいたので構造体で、と書いたの
ですが、元の書き込みを確認したら、
 BNG01,10
 BNG01,5
なのですね。

確認不足、失礼いたしました。


No.3936

Re:ファイルの中身のソート
投稿者---たかし(2002/12/18 00:18:19)


書き方が曖昧でした。
ファイルから読み込んだレコードを確保してソートするために構造体に
入れていたのですが、読み込むレコードの方が多くて、もうこれ以上、
構造体に入れることができなくてそこで落ちていました。
UNIX-Cで、HP-UXです。

最終的には、2つ目の値でソートをとも考えています。
まずは、1つ目でソートができたら、2つ目のソートをと。

No.3953

Re:ファイルの中身のソート
投稿者---かずま(2002/12/19 11:21:37)


> ファイルから読み込んだレコードを確保してソートするために構造体に
> 入れていたのですが、読み込むレコードの方が多くて、もうこれ以上、
> 構造体に入れることができなくてそこで落ちていました。

構造体の配列を用意しておいて、そこにファイルのデータを入れようとしたが、
配列のサイズが固定で、入れることができなくなってしまったということですね。

まず、落ちないように、配列の添え字は常にチェックしましょう。

それから、入れる場所がなくなったら、動的に確保すればよいというのが私の
考えでしたが、このスレッドの最初のほうを読み直してみると、動的確保の
意味が皆さんと異なっていたようです。

自動変数で、struct Record data[100]; とすると、100個までしか読めない。
struct Record data[10000]; とすると、自動変数はスタック上に取られる
ので、そんなに大きな領域を取ることができない。そこで、
struct Recode *data; data = malloc(10000 * sizeof(struct Recode));
というように、ヒープ上に動的に確保すればよい。すなわち、動的確保といっ
ても、最初に 1回実行するだけ。というのが、ともじさんや、たかしさんの
考えられていたことなのではないでしょうか。

私の動的確保とは、ひとつレコードを読むたびに、その領域を新たに malloc
や strdup で確保する。また、配列は、ポインタの配列にしておいて、realloc
で、領域を拡張するということだったわけです。これで、メモリの許す限り
最大限のデータを読み込むことができます。

別のやり方もありまして、まず、ファイルを全部読んでみて個数だけを取得する。
その個数分の配列を動的に確保すれば、あとは、もう一度ファイルを読み直して
すべてのデータを配列に格納することができます。

No.3959

返事ありがとうございます。
投稿者---たかし(2002/12/19 14:30:51)


ともじさん、かずまさんいろいろとアドバイスありがとうございます。
おっしゃるとおり、いろいろとやりかたはあると思います。
よいものでやりたいと思います。

No.3962

Re:ファイルの中身のソート
投稿者---ともじ(2002/12/19 23:15:54)


>> ファイルから読み込んだレコードを確保してソートするために構造体に
>> 入れていたのですが、読み込むレコードの方が多くて、もうこれ以上、
>> 構造体に入れることができなくてそこで落ちていました。

大容量のデータのソートで動的に領域を確保しても領域が不足する場合
には、外部ソートを使う方法もあります。
(かずまさんがご提示の方法でもダメなときですね。)

これは、とりあえずソートできる量に分割してソートをして複数のファイル
に格納します。それから、複数のファイルを同時に読み込みながら
更にマージしていく方法です。
たとえば、
 ファイル1:1,4,7,10…
 ファイル2:2,5,6,11…
なら、両方のデータを比較し、小さい方から書き込んでいきます。
 マージファイル:1,2,4,5,6,7,10,11…