C言語関係掲示板

過去ログ

No625 ファイルの内容をひっくり返す

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

fgets・fputsを使って
投稿者---BEE(2003/05/13 21:31:05)


こんにちは。
ファイルの処理に関しての勉強をしているのですが、
そこでの演習で分からないことがありますので、質問をさせていただきます。

main関数で2つの引数(ファイル名)を受け取って、
一番目のファイルの内容をひっくり返したものを、
二番目のファイルの内容とする、というものプログラムです。

(例えば、a.txtとbtxtというファイルがあり、
a.txtの内容が
「This is a pen.
This is a eraser.」
 であり、それを
「.nep a si sihT
.resare a si sihT」
 といった様にし、それをb.txtに書き込む)

とりあえず、一行目だけを入れ替えるソースをかいてみたのですが、
42行目、rev関数内の、
if (fgets(line,100,fp)!=EOF){
というところで、「移植性の無いポインタ変換だ」
と言われるエラーが出てしまいます。
どういう意味なのでしょうか。

そして、続けて二行目以降をひっくり返す手法がどうも分かりません。
よろしければ、ご教授頂けないでしょうか。
fputsで、改行をどのようにしていいのか分からないです…。
また、このソースでいうrev関数内で、
一行目に引き続き二行目を入れ替える、
というのはどのようにすれば良いのでしょうか。


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

char *rev(char *a);

void main(char *argv[]){
  char *a;
  FILE *fp,*fq;

  if((fp=fopen(argv[1],"r"))==NULL){
    printf("FILE OPEN ERROR\n");
  }
    else{
      a= rev(argv[1]);
      fq=fopen(argv[2],"a");
      if(fputs(a,fq)!=EOF){
	printf("FILE WRITE OK !!\n");
      }
      else{
	printf("FILE WRITE ERROR !!\n");
      }
      fclose(fp);
    }
}

char *rev(char *a){
  int i,j;
 char line[101],z,*p;
FILE *fp;
 if((fp=fopen(a,"r"))==NULL){
   printf("FILE OPEN ERROR\n");
 }
 else{
     if (fgets(line,100,fp)!=EOF){
       for(i=0;i<100;i++){
	 for(j=99;i<j;j--){
	   z=line[j];
	   line[j]=line[j-1];
	   line[j-1]=z;
	 }
       }
     }
     else{
       printf("FILE WRITE ERROR\n");
     }   
     fclose(fp);
   }
 p=line;
 return p;
}


No.6326

Re:fgets・fputsを使って
投稿者---通りすがり(2003/05/13 22:38:22)


読みきって無いですが少なくともこの処理のロジックは
考え直すべきです。これではループしないので
少なくともfgetsは一行目しか読まないというかエラーは
EOFと比較してるからでは?って一行だけって書いてありましたね。
forのループは何文字であっても最後までループするわけで・・・
この場合読み込んだ行の長さをiの終了条件にするべきだと
思います。

if (fgets(line,100,fp)!=EOF){
for(i=0;i<100;i++){ 
for(j=99;i<j;j--){
z=line[j];
line[j]=line[j-1];
line[j-1]=z;
}
}


No.6335

Re:fgets・fputsを使って
投稿者---BEE(2003/05/14 00:21:06)


ご返信ありがとうございます。
fgetsに対しての戻り値をEOFにしてたのがマズかったのですね。
NULLでした、初歩的なミスで申し訳ないです。
そして、ループさせるためにこうしてみました。

ただ、読みこんだ文字列の値をどのようにして求めて良いか分からないです。
例えば、\nが現れるまで繰り返しgetcを行う、
なんていう風にやるのでしょうか。


while (fgets(line,100,fp)!=NULL){
for(i=0;i<100;i++){
for(j=99;i<j;j--){
z=line[j];
line[j]=line[j-1];
line[j-1]=z;
}
}
}

No.6327

Re:fgets・fputsを使って
投稿者---しんちー(2003/05/13 22:58:35)


fgets 行を読む関数なので、返り値としてEOFを返すことはありません。
関数の仕様を調べましょう。
fgets は改行コードごと読み込むので、fputs のときはそれをうまく使ってやればよいのではないでしょうか。

No.6330

Re:fgets・fputsを使って
投稿者---とおりすがり(2003/05/13 23:09:03)


>fgets 行を読む関数なので、返り値としてEOFを返すことはありません。
>関数の仕様を調べましょう。

ホームページ第17章 ファイル入出力で解説されていることです。
まずはそれを見ましょう。

No.6337

Re:fgets・fputsを使って
投稿者---BEE(2003/05/14 00:29:08)


ありがとうございます、と同時に、先に見ておくべきでした。
すみません。

No.6336

Re:fgets・fputsを使って
投稿者---BEE(2003/05/14 00:28:26)


EOFを返すことが出来ない、ごもっともです。
お恥ずかしいミスでした。

>fgets は改行コードごと読み込むので、
fputs のときはそれをうまく使ってやればよいのではないでしょうか。

考えてみました。
こんな具合でしょうか。


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

void rev(char *a,char *b);

void main(char *argv[]){

FILE *fp;

if((fp=fopen(argv[1],"r"))==NULL){
printf("FILE OPEN ERROR\n");
}
else{
rev(argv[1],argv[2]);

fclose(fp);
}
}

void rev(char *a,char *b){
int i,j,m;
char line[101],z,*p,*n;
FILE *fp,*fq;
if((fp=fopen(a,"r"))==NULL){
printf("FILE OPEN ERROR\n");
}
else{

while (fgets(line,100,fp)!=NULL){
for(i=0;i<100;i++){
for(j=99;i<j;j--){
z=line[j];
line[j]=line[j-1];
line[j-1]=z;

}
}
n=line;
fq=fopen(b,"a");
if(fputs(n,fq)!=EOF){
printf("FILE WRITE OK !!\n");
}
else{
printf("FILE WRITE ERROR !!\n");
}

}

fclose(fp);
}

しかし、これではバグが起きてしまいました。
どこか使用が違うのでしょうか。
fputsでは改行が出来ないことが問題でしょうか…。

No.6339

Re:fgets・fputsを使って
投稿者---とおりすがり(2003/05/14 00:52:04)


どんなバグが出たのでしょうか?
答える側がいちいちビルド・実行して確認しなくても
いいように説明が欲しいところです。

あと左詰でわかりにくいプログラムですが、rev関数は
{}の数が合っていないのでは?
おそらくwhileの閉じ括弧あたり。(見間違いならすみません)
それから*p、mは使用されてないように見えますが?

No.6341

Re:fgets・fputsを使って
投稿者---BEE(2003/05/14 01:18:37)


すみません。
バグというのは、コンパイルはうまくいくのですが、
それから実行すると、DOS窓が強制終了になってしまうのです。

>左詰でわかりにくいプログラムですが

<pre>タグが上手い具合に使えないんですよ…。
半角でキッチリ入力しているのですが。
次はなんとかいたします。

>おそらくwhileの閉じ括弧あたり。(見間違いならすみません)
>それから*p、mは使用されてないように見えますが
本当ですね。
初歩的なミスばかりで申し訳ないです。
直したソースを載せてみます。
fputsをfprintfに変えてみました。
しかし、やはりDOS窓が強制終了になるバグが起きてしまいます。


<pre>
#include&lt;stdio.h&gt;
#include&lt;stdlib.h&gt;

void rev(char *a,char *b);

void main(char *argv[]){
FILE *fp;
if((fp=fopen(argv[1],&quot;r&quot;))==NULL){
printf(&quot;FILE OPEN ERROR\n&quot;);
}
else{
rev(argv[1],argv[2]);
fclose(fp);
}
}

void rev(char *a,char *b){
int i,j;
char line[101],z,*p,*n;
FILE *fp,*fq;
if((fp=fopen(a,&quot;r&quot;))==NULL){
printf(&quot;FILE OPEN ERROR\n&quot;);
}
else{
while (fgets(line,100,fp)!=NULL){
for(i=0;i&lt;100;i++){
for(j=99;i&lt;j;j--){
z=line[j];
line[j]=line[j-1];
line[j-1]=z;
}
}
n=line;
fq=fopen(b,&quot;a&quot;);
fprintf(fq,&quot;&amp;s\n&quot;,n);
}
fclose(fp);
}
}</pre>
やはり<pre>タグがうまくいかないです…。


No.6342

Re:fgets・fputsを使って
投稿者---しんちー(2003/05/14 01:19:56)


反転の処理は…そっか、バブルソートみたいにしてやってるんですね。
アルゴリズムを考えるときは具体的な簡単な例でやってみるといいでしょう。
今回は、とおりすがりさんが既に仰ってるように、常に100文字分を反転してるのが間違っていると考えられます。
fputs は改行を自動的にしないだけで、文字列中の '\n' は正しく処理されます。

最後に: 質問される方一般へなんですが、うまくいかないときは一般には
 (1) 環境 (OS名とかコンパイラ名とか)
 (2) エラーやwarningならその表示
 (3) バグなら、入力と、仕様と、期待される出力と、実際の出力
は載せるべきでしょう。

No.6347

Re:fgets・fputsを使って
投稿者---BEE(2003/05/14 10:57:02)


>しんちーさん
確かに僕のアルゴリズムでは無駄に長かったですね。
そこで、最初の文字と最後の文字を入れ替えるというアルゴリズムで
書いてみました。


void rev(char *a,char *b){
  int i,j;
 char line[101],z,*p,*n;
FILE *fp,*fq;
 if((fp=fopen(a,"r"))==NULL){
   printf("FILE OPEN ERROR\n");
 }
 else{ 
     while (fgets(line,100,fp)!=NULL){
       for(i=0;i<100;i++){
	   z=line[i];
	   line[i]=line[100-i];
	   line[100-i]=z;  
       }
	   n=line;
	   fq=fopen(b,"a");
	   if(fputs(n,fq)!=EOF){
printf("FILE WRITE OK !!\n");
}
else{
printf("FILE WRITE ERROR !!\n");
}

     }
     fclose(fp);
   }
}



環境を書いていなかった点について、お詫びいたします。
OS:WindowsME
コンパイラ:Borland C++ 5.5
エラー:「パラメータ 'argc' は一度も使用されない(関数 main )」という
     警告が表示され、無理にコンパイルして実行すると、
     DOS窓が強制終了されてしまう。
入力は引数「unix.txt result.txt」をとって、
unix.txtの内藤を反転させた内容をもつresult.txtを生成する、
という結果が期待されます。

No.6348

Re:fgets・fputsを使って
投稿者---こん!(2003/05/14 11:33:19)


>エラー:「パラメータ 'argc' は一度も使用されない(関数 main )」という
>     警告が表示され、無理にコンパイルして実行すると、
>     DOS窓が強制終了されてしまう。

これは下のかずま氏の指摘で直るんじゃないの?

No.6351

Re:fgets・fputsを使って
投稿者---BEE(2003/05/14 12:06:39)



仰るとおり、main関数の始めに、
if(argc==3){
と付け加えたことによってこの点は直りました。
ありがとうございます。

No.6343

Re:fgets・fputsを使って
投稿者---かずま(2003/05/14 01:25:34)


> void main(char *argv[]){

こんなことを書いているようじゃ、何も始まりません。
C の入門書で最初からやり直すことをお勧めします。

どうしても、ファイルの処理を勉強したいというのなら、
まず、2つのファイルをオープンし、1行ずつコピーするだけの
プログラムを書いてみてください。

ということで、演習の解答としては不適当なプログラムを披露しましょう。
#include <stdio.h>

FILE *i, *o;

int rev(void)
{
    int c = getc(i);
    return c != EOF && c != '\n' && (rev(), putc(c, o)), c;
}

int main(int c, char *v[])
{
    if (c == 3 && (i = fopen(v[1], "r")) && (o = fopen(v[2], "w")))
        while (rev() != EOF) putc('\n', o);
    return 0;
}
こんなプログラムを書いてはいけません。

No.6349

Re:fgets・fputsを使って
投稿者---BEE(2003/05/14 11:48:18)


>かずまさん
void main(int argc,char *argv[])とすると、
「パラメータ 'argc' は一度も使用されない(関数 main )」
とされてしまいます。
これは、かずまさんが書いてくださったソースのように、
if (c == 3 && (i = fopen(v[1], "r")) && (o = fopen(v[2], "w")))
のcのように使うと良いのでしょうか。

一行コピーするプログラムを書いてみました。

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

void rev(char *a,char *b);

void main(int argc,char *argv[]){
  FILE *fp;
  if((fp=fopen(argv[1],"r"))==NULL){
    printf("FILE OPEN ERROR\n");
  }
    else{
      	rev(argv[1],argv[2]);
      fclose(fp);
    }
}

void rev(char *a,char *b){
  int i,j;
 char line[101],z,*p,*n;
FILE *fp,*fq;
 if((fp=fopen(a,"r"))==NULL){
   printf("FILE OPEN ERROR\n");
 }
 else{ 
      if(fgets(line,100,fp)!=NULL){
	   n=line;
	   fq=fopen(b,"a");
	   if(fputs(n,fq)!=EOF){
printf("FILE WRITE OK !!\n");
}
else{
printf("FILE WRITE ERROR !!\n");
}
}
     }
     fclose(fp);
   }

かずまさんが書いてくださった、誤りのソースは、
int rev(void)内の意味が良く分からないのですが。
main関数に、真理値とcを返すんですよね?

No.6372

Re:fgets・fputsを使って
投稿者---BEE(2003/05/14 23:33:53)


う〜ん…。
どうしても、開いたファイルの一行目の文字列の長さを
どのように求めていいのか分かりません。
strlenを使うとすると、int型の変数aを宣言したとして、
a=strlen(?)
?のところにはどのような記述をすれば良いのでしょうか。
fgetsでは引数が定まらないからだめですし…。
例えば、a.txtというファイルがあり、その内容が
I am a man.
You are girl.
という内容であれば、一行目の文字列の大きさ:12を
とってくるにはどのようにすると良いのでしょうか。

No.6374

Re:fgets・fputsを使って
投稿者---通りすがり(2003/05/14 23:48:38)


>う〜ん…。
>どうしても、開いたファイルの一行目の文字列の長さを
>どのように求めていいのか分かりません。
>strlenを使うとすると、int型の変数aを宣言したとして、
>a=strlen(?)
>?のところにはどのような記述をすれば良いのでしょうか。
>fgetsでは引数が定まらないからだめですし…。
>関数を使って測るならstrlenでいいと思います。
fgetsが読んだものの長さを測ればいいんですよ。
最初のプログラムで言えばlineかな

No.6376

Re:fgets・fputsを使って
投稿者---BEE(2003/05/15 00:58:51)


なるほど。
ありがとうございます。
文字列の長さを調べてからfgetsする、
という考えしか頭にありませんでした。

これで、プログラムも完成したかな、と思い、
実行してみたのですが、どうも思ったような結果が得られません。
a.txt b.txt
を引数として実行し、
a.txtの内容は
abcd
dcba
としました。 期待する結果としては、b.txtを生成し、その内容が
dcba
abcd
となることなのですが、b.txtは生成されはしたものの、
内容が何もない、つまり書き込まれていませんでした。
以下のようにしたのですが…。


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

void rev(char *a,char *b);

void main(int argc,char *argv[]){
  FILE *fp;
 	if(argc==3){
 	 if((fp=fopen(argv[1],"r"))==NULL){
    printf("FILE OPEN ERROR\n");
  }
    else{
      	rev(argv[1],argv[2]);
      fclose(fp);
    }
}
else{
	;
}
}

void rev(char *a,char *b){
  int c,i,j;
  char line[101],z,*p,*n;
  FILE *fp,*fq;
	fp=fopen(a,"r");
     while (fgets(line,100,fp)!=NULL){
		 c=strlen(line);
       for(i=0;i<c;i++){
	   z=line[i];
	   line[i]=line[c-i];
	   line[c-i]=z;  
       }
	   n=line;
	   fq=fopen(b,"a");
	   if(fputs(n,fq)!=EOF){;
}
else{
		printf("FILE WRITE ERROR !!\n");
     }

   }
  printf("FILE WRITE OK !!\n");
  fclose(fp);
   
  
}




WinME,Borland C++ 5.5でコンパイルしました。

No.6386

Re:fgets・fputsを使って
投稿者---かずま(2003/05/15 12:00:47)


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

void rev(char *s, int n)
{
    int i, j;
    char c;

    for (i = 0, j = n - 1; i < j; i++, j--) {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}

int main(int argc, char *argv[])
{
    FILE *fin, *fout;
    char buf[1024];

    if (argc != 3) {
        printf("usage: %s infile outfile\n", argv[0]);
        return 1;
    }
    fin = fopen(argv[1], "r");
    if (fin == NULL) {
        printf("can't open %s\n", argv[1]);
        return 1;
    }
    fout = fopen(argv[2], "w");
    if (fout == NULL) {
        printf("can't create %s\n", argv[2]);
        return 1;
    }
    while (fgets(buf, sizeof buf, fin) != NULL) {
        rev(buf, strlen(buf) - 1);
        fputs(buf, fout);
    }
    fclose(fin);
    fclose(fout);
    return 0;
}


No.6400

Re:fgets・fputsを使って
投稿者---BEE(2003/05/15 17:09:30)


なるほど、文字列をひっくり返すアルゴリズムは、
二つの変数を使って表したほうが良いですね。
私のものは、二回ひっくり返すことになっていました。

一つ分からないところがあるのですが、
fout = fopen(argv[2], "w");
と、wモードでopenされていますが、
これは上書きのモードであるのに、
きちんと改行後、続けて書いてくれるのはどうしてなのでしょうか。

それと、私のプログラムでは、何がマズいのでしょうか。
かずまさんの書いてくださったものと比べてみたのですが、
分かりませんでした…。