🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

4回答

3370閲覧

EOFの使い方が分かりません。

ht3433

総合スコア19

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2019/10/09 05:42

前提・実現したいこと

C言語で、EOFの使い方を調べてもよく分かりません。
下記のソースコードでは、
./tr abc def
abc
def
のように、文字の変換が正常に動作していましたが、
EOFをソースコードに加えてみると、
./tr abc def
abc
aec
のように、文字の変換が正常に動作しませんでした。

お力添えいただければ幸いです。

該当のソースコード

C言語

1 2 3ソースコード 4#include<stdio.h> 5#include<string.h> 6#include<ctype.h> 7 8int main(int argc, char *argv[]){ 9 10// 入力する文字の変数を定義 11char moji1[100]; 12int c; 13size_t i; 14size_t j; 15// 何度も文字を入力可能にする 16while( (= getchar() ) != EOF ){ 17putchar(); 18memset(moji1, 0, sizeof(moji1)); 19 20// 文字の入力 21scanf( "%c", moji1 ); 22 23// 入力した文字分だけ繰り返す 24for( i = 0; i < strlen( moji1 ); i++ ){ 25// trの変換先の文字分だけ繰り返す 26for( j = 0; j < strlen( argv[1] ); j++ ){ 27// 入力した文字と変換先の文字が同じか判定 28     if( moji1[i] == argv[1][j] ){ 29// trの変換先と変換元の文字が同じか判定 30if( argv[1][j] != argv[2][j] ){ 31printf( "%c", argv[2][j] ); 32i++; 33}else{ 34printf( "%c", moji1[i] ); 35 36} 37} 38} 39if( moji1[i] != argv[1][j] ){ 40printf( "%c", moji1[i] ); 41} 42} 43} 44return( 0 ); 45}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2019/10/09 06:30

上記、本質問とは論点は違いますけれども。(違う視点と説明から見ると分かる場合もあるのでは、との期待からのご案内です)
cateye

2019/10/09 06:52 編集

インデントなんとかならんのか?→https://webkaru.net/clang/indent/ あと、while( ( c = getchar() ) != EOF )とputchar( c );のcが全角d^^
ht3433

2019/10/09 07:14

回答していただき、ありがとうございます! EOFがどういうものなのかは分かりましたが、使い方がいまいちわからないです。 理解力がなくて大変申し訳ないですが、詳しく教えていただけるとありがたいです。
cateye

2019/10/09 08:27 編集

答えになっているのか?・・・多分に主観が入っています・・・ EOF(end of file)の考えは、もともとはUNIXで持ち込まれたものだと思います。 (UNIXでは、全ての入出力をファイルとして扱っていた→仮想化) で、C言語はUNIXの実装言語ですから、そこから来ているのだと思います。 で、EOFの使い方ですが、ファイル(入出力)が終了したことを知らせるために、ファイル読み書き時に入力(出力)が終了したことをシステムが通知するためにEOFを返してきます。従って、EOFを検出した場合、読み込みを終了させる必要が有ります。文字の読み込みでも、行の読み込みでもEOFが帰ってきたら、(読み込み)処理を終わらせて後続の処理に移る必要が有ります。EOFはキーボード入力ならCtl-DあるいはCtl-Cで送れます。(Windowsはどうだろう?)
dodox86

2019/10/09 08:57

>cateyeさん 回答欄に書かれてはいかがでしょうか。ただ、CTRL-CはBREAKだと思います。プログラムはDOS、Windowsのコマンドプロンプト、UNIX端末でも強制終了かと。DOS/WindowsコマンドプロンプトですとCTRL-Z(0x1b)ですね。
cateye

2019/10/09 09:17

ご助言有難うございます。・・・コメントの追加になってしまった^^;
dodox86

2019/10/09 11:01

CTRL-ZのEOF間違えました。 正:CTRL-Z 0x1a 誤:CTRL-Z 0x1b (ESC) 0x1bはエスケープシーケンス始まりでした。
guest

回答4

0

ベストアンサー

自分が何をするのかをちゃんと整理しないでプログラムをいじってしまって混乱に陥っているように思います。(いじる元のプログラムもちゃんと理解していなかったのかも知れませんが)

質問のプログラムをベースにプログラムを書いてみましたが、コメント空行有りで20行でした。質問のプログラムのおよそ半分は無駄、というか間違いと言ってしまっても...

擬似言語で書くならば

Text

11文字入力して EOFでなかったら (EOFだったら終わり) 2 変換元の文字を探索 3 入力文字と変換元の文字が一致したら 4 変換先の文字に置き換えて 5 探索終わり 6 文字を表示 7繰り返し 8 9終わり

こんな感じでしょうか。

端折りましたが、
./tr abc (パラメータが足りない)
./tr abcdef ghi (変換先の文字数が少ない)
なんて場合も妙なことが起こらないように対策する必要があるでしょう。

これだけ時間を使って解決しない時は、一度真っ白な状態から作り直してみるのも一案ではないでしょうか。

投稿2019/10/17 12:46

thkana

総合スコア7703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ht3433

2019/10/21 01:14

コメントしていただき、ありがとうございます。 thkanaさんのおっしゃるとおり、一度整理してプログラムをかいてみると、上手くいきました! ありがとうございました。
guest

0

https://teratail.com/questions/214923#reply-316588(2つの文字列の変換をしたいです)
このコードは参考にならなかったようですね。
EOF まで読み込むので Linux なら Ctrl-D で終了したのに。

では、変換テーブルを使わないやり方にしてみます。

C

1#include <stdio.h> // getchar, putchar, fprintf 2#include <string.h> // strlen 3 4int main(int argc, char *argv[]) 5{ 6 int n, c, t, i; 7 if (argc != 3 || (n = strlen(argv[1])) != strlen(argv[2])) { 8 fprintf(stderr, "usage: %s string1 string2\n", argv[0]); 9 return 1; 10 } 11 while ((t = c = getchar()) != EOF) { 12 for (i = 0; i < n; i++) 13 if (c == argv[1][i]) 14 t = argv[2][i]; 15 putchar(t); 16 } 17}

これは参考になりますか?
分からないところは質問してください。

投稿2019/10/09 16:07

編集2019/10/09 16:09
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ht3433

2019/10/10 05:10

回答していただき、ありがとうございます。 fprintf(stderr, "usage: %s string1 string2\n", argv[0]); というコードですが、これはなにをしているのでしょうか? 理解力がなくて大変申し訳ないのですが、教えていただけるとありがたいです。
kazuma-s

2019/10/10 13:19

その前の行に if (argc != 3 || (n = strlen(argv[1])) != strlen(argv[2])) { とありますが、これが何かは分かっているんですよね。 プログラムを実行するのに必要な引数が正常に与えられているかどうかを チェックしています。fprintf の行では、 引数が不正の場合にエラーメッセージ(正しい使用方法)を出力しています。 エラーメッセージを printf で出力すると、 ./tr abc de <file1.txt >file2.txt のように、入出力をファイルにリダイレクトした場合、出力ファイルに エラーメッセージが入って、エラーであることがすぐには分かりません。 そこで標準出力 stdout ではなく、標準エラー出力 stderr に出力するように しています。 ところで、文字の変換の部分は参考にならなかったんでしょうか?
ht3433

2019/10/15 09:30

回答していただき、ありがとうございます。 返事が遅くなり、大変申し訳ございません。 詳しい説明をしていただき、ありがとうございます。 正直にいいますと、文字の変換の部分は参考にならなかったです。 貴重なお時間を使って回答してくださったのに、このようなことを述べてしまい、申し訳ございません。
guest

0

c = getchar()

で読み込んだ1文字を何も処理せずに直後で

putchar( c );

しているからです。
つまり、

繰り返し開始
1文字読んでそのまま書く
1文字読んで変換対象なら変換してして書く
繰り返し終わり

という処理になっています。

moji1scanfするのをやめて、c = getchar()で入力したcに対して、変換処理を行えばいいと思います。

投稿2019/10/09 14:09

編集2019/10/09 14:31
otn

総合スコア85893

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ht3433

2019/10/15 09:36

回答していただき、ありがとうございます。 scanfをやめて、c = getchar()で入力したcに対して変換をしてみたのですが、1文字目が表示されないようになりました。 何が原因なのでしょうか? なにかヒントをいただけると、ありがたいです ソースコード #include<stdio.h> #include<string.h> #include<ctype.h> int main(int argc, char *argv[]){ // 文字の変数を定義 char moji1[100]; char c; int count = 0; int i; size_t j; // 何度も文字を入力可能にする while( ( c = getchar() ) != EOF ){ memset(moji1, 0, sizeof(moji1)); //文字列をキーボードから入力する for(count = 0; count < 100; count++){ c = getchar(); //改行なら終了 if(c == '\n'){ break; //それ以外なら保存する     }else{ moji1[count] = c; } } // 入力した文字分だけ繰り返す for( i = 0; i < count; i++ ){ // trの変換先の文字分だけ繰り返す for( j = 0; j < strlen( argv[1] ); j++ ){ // 入力した文字と変換先の文字が同じか判定 if( moji1[i] == argv[1][j] ){ // trの変換先と変換元の文字が同じか判定 if( argv[1][j] != argv[2][j] ){ putchar( argv[2][j] ); i++; }else{ putchar( moji1[i] );        } } } if( moji1[i] != argv[1][j] ){ putchar( moji1[i] ); } } } return( 0 ); }
otn

2019/10/15 10:44

getchar()をあちこちで実行しているからです。 while( ( c = getchar() ) != EOF ){ 以外のgetchar()を全部消して、プログラム全体を書き直しましょう。
ht3433

2019/10/17 01:54

回答していただき、ありがとうございます。 早速、 //文字列をキーボードから入力する for(count = 0; count < 100; count++){ c = getchar(); のc = getchar();を消して、 //文字列をキーボードから入力する for(count = 0; count < 100; count++){ にして実行してみたのですが、 ./tr a b a babababababababababababababababababababababababababababababababababababababababababababababababababa という結果になってしまいました。 コードを見返してみたのですが、なぜこうなってしまったのか分かりません。 指摘していただけるとありがたいです。 宜しくお願い致します。
otn

2019/10/17 12:18

「プログラム全体を書き直しましょう。 」と書きましたが、 while( ( c = getchar() ) != EOF ){ まで残して、それ以降全部消して書き直しましょうということです。 最初のプログラム構造が間違っているので、それをベースに書き直しても駄目です。
guest

0

標準入出力を使う限りにおいては、ファイルの終わりというのが存在しないので、EOFというものは来ません。
なので使わないほうがいいです

#実際にはCtrl-CだかCtrl-Dだかを入れると出すことができるということだけど

投稿2019/10/09 05:57

y_waiwai

総合スコア88038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ht3433

2019/10/09 06:10

回答していただき、ありがとうございます! では、Ctrl + Dでプログラムを終了する方法はないのでしょうか?
y_waiwai

2019/10/09 06:12

そうしたいなら、EOFの検出でプログラムを終了させるようにすればよろしい。 まあしかし、それで終了させるというのはちょっと一般的ではないですわな。
dodox86

2019/10/09 07:35

ターミナルからの入力を標準入力のファイルとした場合、CTRL-Z/CTRL-Dの入力をもってEOFとするのは一般的といえると思います。
dodox86

2019/10/09 07:38

「プログラムを終了させる用途でEOFを使う」ということが一般的ではない、と言う意味であれば同意です。
Zuishin

2019/10/09 07:45

Node.js が EOF で終わってた気がします。昔は標準入力を扱うものはだいたいそれで終わってたような。C の初心者向けの解説でも fgets で NULL が返るまでループするのは割とポピュラーでした。
ht3433

2019/10/09 08:48

Ctrl + Dでプログラムを終了させることはできるのですが、 putchar( c );の影響で、文字の変換がまったくされません。 putchar( moji1 );とすると、 エラー: ‘char*’ から ‘int’ への無効な変換です [-fpermissive] putchar( moji1 ); というエラーがでます。 どうしたらよいでしょうか? お力添えいただければ幸いです。
y_waiwai

2019/10/09 08:55

それ、使い方間違ってます moji1というのは配列です > printf( "%c", moji1[i] ); のように、配列の要素を指定しましょう
y_waiwai

2019/10/09 08:57

もちっと詳しい解説を。 putcharという関数は、1文字の出力関数です。 とうぜん、引数には文字が入ります。 そこに、配列(あるいはポインタ)を入れるとエラーになりますね
ht3433

2019/10/10 01:28

回答していただき、ありがとうございます。 printf( "%c", argv[2][j] ); のところを putchar( "%c", argv[2][j] ); としたのですが、 エラー: ‘const char*’ から ‘int’ への無効な変換です [-fpermissive] putchar( "%c", argv[2][j] ); ^ kadai.cc:26:49: エラー: too many arguments to function ‘int putchar(int)’ In file included from kadai.cc:1:0: /usr/include/stdio.h:580:12: 備考: ここで宣言されています extern int putchar (int __c); という内容のエラーがでてしまいます。 これは、配列の中の1文字を出力することはできないということでしょうか?
Zuishin

2019/10/10 01:32

Node.js の対話モードが EOF で終わるというのはデマでした。Python は EOF で終わりました。どうでもいい情報ですが一応訂正しておきます。
dodox86

2019/10/10 02:29 編集

>ht3433さん どうも迷走しているような。 > printf( "%c", argv[2][j] ); のところを putchar( "%c", argv[2][j] ); なぜそうコーディングするのか。putcharはprintfとは別物ですよね。関数リファレンスを読みましょう。 linux なら ターミナル上でmanコマンドやinfoコマンドでputcharのリファレンスが出ると思います。 例: $ man putchar manページがインストールされていなかったり、英語表示で困ったら書籍なりWEBなりで確認しましょう。
ht3433

2019/10/10 04:13

回答していただき、ありがとうございます。 関数レファレンスを調べたところ、確かにputcharとprintfは別物でした。 となると、printfを使わずに、getcharとputcharをつかって、EOFをつくらなければならないですよね?
dodox86

2019/10/10 04:45

putcharを使ってEOFを作る。。。ちょっと違います。EOFとは、getcharを使うのであれば、ファイルの末端まで読んだことを示す「ファイル終了のお知らせ」です。端末からのキー入力であればそのキー入力全体が「ファイル」にあたります。それがgetcharの返り値/戻り値EOF(-1)となるので、それで入力の終了が分かる、ということです。たくさん回答をいただいているのですから、実際に試してみましょうよ。
dodox86

2019/10/10 05:21 編集

一応補足をしておきますと、ht3433さんのもとのコードのようにscanfとprintfでもやってやれないことはありません。しかしながら1文字ずつの入出力なのだから、一般的にgetchar/putcharを使うのが適切なので、私含め、他の回答者さんらもそれらを使っている、というわけです。C言語の一般的な入門書でもそうなっています。(参考: fgetc, fputc) y_waiwaiさんの回答へのコメント欄なので、回答含め、これ以上は控えたいと思います。(失礼いたしました)
thkana

2019/10/17 12:03

> 標準入出力を使う限りにおいては、ファイルの終わりというのが存在しないので、EOFというものは来ません。 一応。標準入力にファイルをリダイレクトで流し込んで終わったり、パイプで繋いでいたのを元で閉じるとEOFになります。それを考えるとEOFで終わる動きも有りでは。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問