【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は謹んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板2こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.19006

2つのファイルのソート
投稿者---・・・(2005/01/04 15:03:32)


ファイルaとbに1行に1つの整数値が書かれており、しかも
小さい順にソートされています。aとbの整数値を合わせてソート
させて表示したいのですがうまくいきません。
考え方はaとbを1行ずつ比べて小さいほうを出力して大きいほうを
残して、残した大きいほうと新たな小さい数を比べる・・・
を繰り返すだと思うんですが、どのようにすればいいのでしょうか?


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:2つのファイルのソート 19009 あかま 2005/01/04 15:56:20


No.19009

Re:2つのファイルのソート
投稿者---あかま(2005/01/04 15:56:20)


>考え方はaとbを1行ずつ比べて小さいほうを出力して大きいほうを
>残して、残した大きいほうと新たな小さい数を比べる・・・
>を繰り返すだと思うんですが、どのようにすればいいのでしょうか?
考え方はそれで完璧。
マージソートといいます。

結局、なにがうまくいかないのですか?
それを書かなければ答えようがありません。
最低限、自分で作ったうまくいかないプログラムを載せてください。


この投稿にコメントする

削除パスワード

No.19011

Re:2つのファイルのソート
投稿者---・・・(2005/01/04 16:59:19)


自分で作ってみたのはこんな感じです。
このプログラムでは実行はできません。
お聞きしたいことは、どうすればプログラムを
完成できるかです。
<pre>
#include&lt;stdio.h&gt;
#include&lt;stdlib.h&gt;

int main()
{
FILE *fp,*fp2;
char c[128],d[128],e[128],f[128];
int m,n,k,l;

fp=fopen(&quot;a.txt&quot;,&quot;r&quot;),fp2=fopen(&quot;b,txt&quot;,&quot;r&quot;);

while(fgets(c,128,fp)!=NULL){
for(m=0;c[m]!='\n'; ){
for(k=0;c[m]!='\n';m++){
while(fgets(d,128,fp2)!=NULL){
for(n=0;d[n]!='\n'; ){
for(l=0;d[n]!='\n';l++){
e[k]=c[m];
f[l]=d[n];
}
e[k]='\0';
f[l]='\0';
if(atoi(e)&gt;=atoi(f)){
printf(&quot;%d&quot;,atoi(f));
l++;
}else{
printf(&quot;%d&quot;,atoi(e));
k++;
}
}
}
}
}
}
fclose(fp);
fclose(fp2);
return 0;
}
</pre


この投稿にコメントする

削除パスワード

No.19013

Re:2つのファイルのソート
投稿者---あかま(2005/01/04 17:52:34)


んー、残念かなこのプログラムはforとwhileでまわしすぎですね。
あなたの考えた流れを少し細かくするとこうなります。

1.aにfpから読み込み
2.bにfp2から読み込み

3.aとbを比較小さいほうを出力
4.小さいほうを読み込み。読み込めれば3へ。読み込めなければ5へ(片方のファイルが終了)

5.残ったファイルの内容をfor文で最後まで出力

ここでforかwhileを使っているのは、3.4.で1つ、5.で1つですので2つあれば済むはずです。
また、入れ子にもなっていません。
もう一度プログラムの流れを考え直してみてはどうでしょうか?

また、読み込むときは
fgets(c,128,fp);
sscanf(c,"%d",&m);

とするか
fscanf(fp," %d",&m);
とすると楽かと思います。
今回は整数に変換するだけなのでatoiでもいいのですが。
(もしかしてプログラムで読み込んだ行の'\n'の削除をやっていますか?削除しなくてもatoiは動きますよ)



この投稿にコメントする

削除パスワード

No.19015

Re:2つのファイルのソート
投稿者---・・・(2005/01/04 18:42:56)


4.小さいほうを読み込み。読み込めれば3へ。読み込めなければ5へ(片方のファイルが終了)
について質問なんですが読み込めるかどうかを判断するには
どうすればいいんですか?



この投稿にコメントする

削除パスワード

No.19016

Re:2つのファイルのソート
投稿者---あかま(2005/01/04 18:47:19)


>4.小さいほうを読み込み。読み込めれば3へ。読み込めなければ5へ(片方のファイルが終了)
>について質問なんですが読み込めるかどうかを判断するには
>どうすればいいんですか?
fgetsならNULLが返ります。
fscanfなら読み込んだ数(この場合は1)が返ります。



この投稿にコメントする

削除パスワード

No.19017

Re:2つのファイルのソート
投稿者---・・・(2005/01/04 18:59:10)


あかまさんありがとうございました。
とりあえず今日と明日で考えてみます。
わからないときはまた投稿しますのでよろしくお願いします。


この投稿にコメントする

削除パスワード

No.19024

Re:2つのファイルのソート
投稿者---επιστημη(2005/01/05 09:53:08)


>わからないときはまた投稿しますのでよろしくお願いします。

ご参考。C++なら脱力するほどに簡単。

#include <fstream>    // ifstream, ofstream
#include <iterator>   // istream_iterator, ostream_iterator
#include <algorithm>  // merge

int main() {
  std::ifstream one("one.txt");
  std::ifstream two("two.txt");
  std::ofstream out("out.txt");
  std::merge(std::istream_iterator<int>(one),
             std::istream_iterator<int>(),
             std::istream_iterator<int>(two),
             std::istream_iterator<int>(),
             std::ostream_iterator<int>(out, "\n"));
  return 0;
}




この投稿にコメントする

削除パスワード

No.19039

Re:2つのファイルのソート
投稿者---RiSK(2005/01/05 13:01:52)


>ご参考。C++なら脱力するほどに簡単。

同じくご参考に。C バージョン。
while の条件が難解になってしまった…
#include <stdio.h>
int main(int argc, char ** argv)
{
    FILE * f1, * f2;
    int ret1, ret2, n1, n2;
    if (argc < 3) {
        return 1;
    }
    if ((f1 = fopen(argv[1], "r")) == NULL) {
        return 1;
    }
    if ((f2 = fopen(argv[2], "r")) == NULL) {
        fclose(f1);
        return 1;
    }
    ret1 = fscanf(f1, " %d", &n1);
    ret2 = fscanf(f2, " %d", &n2);
    while (ret1 == 1 || ret2 == 1) {
        while (ret1 == 1 && (n1 <= n2 || ret2 != 1)) {
            printf("n1\t%d\n", n1);
            ret1 = fscanf(f1, " %d", &n1);
        }
        while (ret2 == 1 && (n1 >= n2 || ret1 != 1)) {
            printf("n2\t%d\n", n2);
            ret2 = fscanf(f2, " %d", &n2);
        }
    }
    fclose(f1);
    fclose(f2);
    return 0;
}



この投稿にコメントする

削除パスワード

No.19081

Re:2つのファイルのソート
投稿者---かずま(2005/01/07 16:01:45)


>   ret1 = fscanf(f1, " %d", &n1);

    ret1 = fscanf(f1, "%d", &n1);

と書かないのはなぜですか?


>   while (ret1 == 1 && (n1 <= n2 || ret2 != 1)) {

    while (ret1 == 1 && (ret2 != 1 || n1 <= n2)) {

と書いたほうがいいと思いませんか?


> while の条件が難解になってしまった…

条件を簡単にすると、

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *f1, *f2;  int r1, r2, n1, n2;

    if (argc != 3) return 1;
    f1 = fopen(argv[1], "r");
    f2 = fopen(argv[2], "r");
    if (!f1 || !f2) return 1;

    r1 = fscanf(f1, "%d", &n1);
    r2 = fscanf(f2, "%d", &n2);
    while (r1 == 1 && r2 == 1)
        if (n1 < n2) printf("%d\n", n1), r1 = fscanf(f1, "%d", &n1);
        else         printf("%d\n", n2), r2 = fscanf(f2, "%d", &n2);
    while (r1 == 1)  printf("%d\n", n1), r1 = fscanf(f1, "%d", &n1);
    while (r2 == 1)  printf("%d\n", n2), r2 = fscanf(f2, "%d", &n2);
    return 0;
}



この投稿にコメントする

削除パスワード

No.19083

Re:2つのファイルのソート
投稿者---RiSK(2005/01/07 23:58:10)


>>   ret1 = fscanf(f1, " %d", &n1);
>
>    ret1 = fscanf(f1, "%d", &n1);
>
>と書かないのはなぜですか?

7.19.6.2 fscanf関数 (snip)  空白類文字で構成される指令は,最初の非空白類文字の直前まで(この文字は読み取らずに残す。),又 はそれ以上読み取ることができなくなるまで,入力読み取りを繰り返し実行する。
これを覚えていたのでスペースを入れていました。 しかし,
 変換指定が[,c又はn指定子のいずれをも含まない場合,(isspace関数で規定される)空白類文字を 読み飛ばす。
ともありますね。よって不要だったようです。調べる機会をありがとうございます。 >> while (ret1 == 1 && (n1 <= n2 || ret2 != 1)) { > > while (ret1 == 1 && (ret2 != 1 || n1 <= n2)) { > >と書いたほうがいいと思いませんか? なぜ,そう聞かれたのか結構悩みました。効率だろうかと考えていましたが,違いますね。 f2 を読んだとき最初から EOF だった場合に不定値の n2 を比較することになってしまいます。 バグでした。 もう一個の while も同じですね。 >条件を簡単にすると、 なるほど…。シンプルだ。 かずまさんのコードはいつも勉強になります。ありがとうございました。 最後に修正版ソース: ret1 = fscanf(f1, "%d", &n1); ret2 = fscanf(f2, "%d", &n2); while (ret1 == 1 || ret2 == 1) { while (ret1 == 1 && (ret2 != 1 || n1 <= n2)) { printf("n1\t%d\n", n1); ret1 = fscanf(f1, "%d", &n1); } while (ret2 == 1 && (ret1 != 1 || n1 >= n2)) { printf("n2\t%d\n", n2); ret2 = fscanf(f2, "%d", &n2); } }



この投稿にコメントする

削除パスワード

No.19095

Re:2つのファイルのソート
投稿者---・・・(2005/01/09 05:10:21)


お世話になってます。
プログラムを真似てfgetsで作ってみたんですがうまく実行されません。
fgetsとsscanfの考え方がおかしいんですかね?
プログラムは下の通りです。
#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE *fp,*fp2;
    char c[128],e[128];
    int a,b;
    fp=fopen("a.txt","r");
    fp2=fopen("b.txt","r");
    fgets(c,128,fp);
    fgets(e,128,fp2);
    sscanf(c,"%d",&a);
    sscanf(e,"%d",&b);
    while(fgets(c,128,fp)!=NULL || fgets(e,128,fp2)!=NULL){
        while(fgets(c,128,fp)!=NULL && (fgets(e,128,fp2)==NULL || a<=b)){
        printf("%d\n",a);
        fgets(c,128,fp);
        sscanf(c,"%d",&a);
        }
        while(fgets(e,128,fp2)!=NULL && (fgets(c,128,fp)==NULL || a>=b)){
        printf("%d\n",b);
        fgets(e,128,fp2);
        sscanf(e,"%d",&b);
        }
    }
    fclose(fp);
    fclose(fp2);
    return 0;
}



この投稿にコメントする

削除パスワード

No.19096

Re:2つのファイルのソート
投稿者---monkey(2005/01/09 10:56:17)


>プログラムを真似てfgetsで作ってみたんですがうまく実行されません。

どのようにうまく実行されないかを説明すべきです。

>fgetsとsscanfの考え方がおかしいんですかね?

そのように推測できたのであれば、これらの関数の使い方や動作について調
べたり試したりすれば、自ら解決できるはずです。

さっとみたところ、「fgets関数を呼び出しがファイルポインタの位置を進
める」ということを理解されていないためのミスだと思われます。
次のプログラムの動作を調べてみて下さい。

// exam.
#include <stdio.h>
#define SIZE 256

void case_a( FILE* fp )
{
    char buff[SIZE];
    char* ret ;

    printf( "Case A ======\n" );
    ret = fgets( buff, SIZE, fp );
    while( ret != NULL ){
        if( ret != NULL ){
            printf( "%s", buff );
            ret = fgets( buff, SIZE, fp );
        }
    }
}

void case_b( FILE* fp )
{
    char buff[SIZE];

    printf( "Case B ======\n" );
    while( fgets( buff, SIZE, fp ) != NULL ){
        if( fgets( buff, SIZE, fp ) != NULL ){
            printf( "%s", buff );
        }
    }
}

int main( void )
{
    FILE* fp;

    fp = fopen( "a.txt", "r" );
    if( fp == NULL ){
        return 1;
    }
    case_a( fp );

    if( fseek( fp, 0, SEEK_SET ) != 0 ){
        return 1;
    }
    case_b( fp );

    return 0;
}



この投稿にコメントする

削除パスワード

No.19097

Re:2つのファイルのソート
投稿者---monkey(2005/01/09 11:03:28)


日本語がヘンでした:

「fgets関数の呼び出しがファイルポインタの位置を進める」ということを理解されていないためのミスだと思われます。


この投稿にコメントする

削除パスワード

No.19098

Re:2つのファイルのソート
投稿者---・・・(2005/01/09 13:11:11)


できました。
条件式で置き換えるか置き換えないかでこんなに差がでるんですね。
ありがとうございました。
もう1つ聞きたいんですがfscanf=fgets+sscanfってことなんですか?


この投稿にコメントする

削除パスワード

No.19102

Re:2つのファイルのソート
投稿者---あかま(2005/01/09 13:43:11)


>もう1つ聞きたいんですがfscanf=fgets+sscanfってことなんですか?
似てはいますが、違います。
ファイルに

"123\n456\n"

という文字列があったとき
fgets(str,256,fp)+sscanf(str,"%d",&a)だと
fgetsでは"123\n"まで読み込まれsscanfでaに123という数値に変換されます。
ファイルに残った文字は"456\n"です。

fscanf(fp,"%d",&a)だと
"123"まで読み込まれaに123という数値に変換されます。
ファイルに残った文字は"\n456\n"です。

残った\nが
http://www9.plala.or.jp/sgwr-t/c/sec05.html#s5-4
↑の「(2)改行文字が残る」ように悪さをするときがあるので、注意が必要です。

また、

"abc\n456\n"

という文字列に同様の操作をかけたときは
sscanfとfscanfでは数値への変換に失敗します。変換に失敗するとファイルポインタが進まないので
残る文字列はそれぞれ

fgets+sscanf="456\n"

fscanf="abc\n456\n"

になります。
もしfscanfをファイルが終了するまでwhileで回していたときは「ずっとファイルポインタが進まない=無限ループ」になるので注意が必要です。

常に正しい入力のときはfscanfで、
なにを入力されるかわからないときはfgets+sscanf
にしたほうが安全です。



この投稿にコメントする

削除パスワード

No.19103

Re:2つのファイルのソート
投稿者---・・・(2005/01/09 13:57:17)


わかりました。微妙な違いなんですね。
ありがとうございました。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧