I/O関数

C 言語には、入出力関数、または略して I/O 関数と呼ばれる、外部デバイスと通信するための関数がいくつか用意されています。入力(インポート)は外部からデータを取得することを指し、出力(エクスポート)はデータを外部に転送することを指します。

キャッシュとバイト ストリーム

厳密に言えば、入出力関数は外部デバイスと直接通信するのではなく、バッファを介して間接的に通信します。このセクションではキャッシュとは何かを紹介します。

通常のファイルは通常、ディスクに保存されます。CPU と比較すると、ディスクからのデータの読み取りまたは書き込みは非常に遅い処理です。したがって、プログラムがディスクを直接読み書きすることは現実的ではなく、コマンドを実行するたびに半日かかる場合があります。 C 言語での解決策は、ファイルを開いている限り、このファイルのキャッシュ領域をメモリ内に設定することです。

プログラムがファイルにデータを書き込むとき、プログラムはまずデータをキャッシュに置き、キャッシュがいっぱいになるまで待ってから、内部のデータをディスク ファイルに一度に書き込みます。この時点ではキャッシュ領域は空であるため、プログラムは新しいデータをキャッシュに入れてプロセス全体を繰り返します。

プログラムがファイルからデータを読み取るとき、ファイルはまずデータの一部をキャッシュに置き、次にプログラムはキャッシュからデータを取得し、キャッシュが空になると、ディスク ファイルが新しいデータをキャッシュに置きます。プロセス全体が繰り返されます。

メモリの読み取りおよび書き込み速度は、ディスクの読み取りおよび書き込み速度よりもはるかに高速です。キャッシュ設計により、ディスクへの読み取りおよび書き込みの回数が減り、プログラムの実行効率が大幅に向上します。さらに、大きなデータ チャンクを一度に移動する方が、小さなデータ チャンクを複数回移動するよりもはるかに高速です。

この読み取りおよび書き込みモードは、プログラムのストリームに似ており、すべてのデータを一度に読み取りまたは書き込みするのではなく、継続的なプロセスです。最初にデータの一部を操作し、キャッシュがデータのこの部分を処理するまで待機してから、データの次の部分を操作します。この処理をバイトストリーム操作と呼びます。

読み取り後はキャッシュが空になるため、バイト ストリームは 1 回だけ読み取ることができ、2 回目は読み取ることができません。これはファイルの読み取りとは大きく異なります。

ファイルの読み取りと書き込みを伴う C 言語の入出力関数は、バイト ストリーム操作です。入力関数はファイルからデータを取得して入力ストリームを操作し、出力関数はデータをファイルに書き込み、出力ストリームを操作します。

printf()

printf() は、画面出力に使用される最も一般的な出力関数です。詳細については、「基本構文」の章を参照してください。

scanf()

基本的な使い方

scanf() 関数は、ユーザーのキーボード入力を読み取るために使用されます。プログラムがこのステートメントに到達すると、プログラムは停止し、キーボードからのユーザー入力を待ちます。ユーザーがデータを入力して Enter キーを押すと、scanf() がユーザーの入力を処理し、変数に保存します。そのプロトタイプはヘッダー ファイル stdio.h で定義されます。

scanf() の構文は printf() と似ています。

scanf("%d", &i);

最初のパラメータはフォーマット文字列で、プレースホルダ (基本的に printf() のプレースホルダと同じ) が配置され、ユーザーの入力を解釈する方法と抽出する必要があるデータの種類をコンパイラに指示します。これは、C 言語のデータは型付けされており、scanf() はデータを処理する前にユーザーが入力したデータ型を事前に知っておく必要があるためです。残りのパラメータは、ユーザー入力を保存する変数です。フォーマット文字列内のプレースホルダと同じ数の変数があります。

上記の例では、「scanf()」の最初のパラメータ「%d」は、ユーザー入力が整数である必要があることを示しています。 %d はプレースホルダー、% はプレースホルダーのマーク、d は整数を表します。 2 番目のパラメータ &i は、ユーザーがキーボードから入力した整数を変数 i に格納することを意味します。

scanf() は値ではなくアドレス、つまり変数 i が指すアドレスを渡すため、変数 (ポインター変数を除く) の前に & 演算子を追加する必要があることに注意してください。ユーザーが入力した値に。ここでの変数がポインタ変数 (文字列変数など) の場合、「&」演算子を追加する必要はありません。

以下は、キーボード入力を複数の変数に一度に読み取る例です。

scanf("%d%d%f%f", &i, &j, &x, &y);

上記の例では、書式文字列 %d%d%f%f は、ユーザーが入力した最初の 2 つが整数で、最後の 2 つが浮動小数点数 (「1 -20 3.4 -4.0e3」) であることを意味します。これら 4 つの値は、4 つの変数 ijxy に順番に代入されます。

scanf() が数値プレースホルダーを処理するとき、スペース、タブ、改行などを含む空白文字が自動的に除外されます。したがって、ユーザーが入力したデータ間の 1 つ以上のスペースは、scanf() によるデータの解釈に影響しません。さらに、ユーザーは Enter キーを使用して入力を複数の行に分割できますが、解釈には影響しません。

1
-20
3.4
-4.0e3

上の例では、ユーザーは 4 行で入力し、結果は 1 行で入力した場合とまったく同じになります。 Enter キーを押すたびに、scanf() は最初のプレースホルダーと一致すると解釈を開始し、次に Enter キーを押すと 2 番目のプレースホルダーから解釈を開始します。

ユーザー入力を処理するための scanf() の原理は、ユーザーの入力が最初にキャッシュに入れられ、Enter キーが押された後、プレースホルダーに従ってキャッシュが解釈されることです。ユーザー入力を解釈するときは、キャッシュが読み取られるか、条件を満たさない最初の文字が検出されるまで、最後の解釈で残った最初の文字から開始されます。

int x;
浮動小数点 y;

//ユーザー入力「-13.45e12# 0」
scanf("%d", &x);
scanf("%f", &y);

上の例では、scanf() がユーザー入力を読み取るとき、%d プレースホルダーは先頭のスペースを無視し、- からデータの取得を開始し、-13 の読み取りを停止します。これは、次の . が続くためです。は整数として有効な文字ではありません。これは、プレースホルダー %d-13 と読み取られることを意味します。

scanf() が 2 回目に呼び出されるとき、前回解釈が停止した場所から読み取りを続けます。今回読み込まれる最初の文字は . です。対応するプレースホルダーは %f であるため、科学的表記法を使用した浮動小数点数形式である .45e12 が読み込まれます。次の # は浮動小数点数として有効な文字ではないため、ここで終了します。

scanf() は複数のプレースホルダーを連続して処理できるため、上記の例は次のように書くこともできます。

scanf("%d%f", &x, &y);

scanf() の戻り値は整数で、正常に読み取られた変数の数を示します。項目が読み取られなかった場合、または一致が失敗した場合は、「0」が返されます。ファイルの終わりを読み取ると、定数 EOF が返されます。

プレースホルダー

scanf() でよく使われるプレースホルダーは以下の通りで、基本的には printf() のプレースホルダーと同じです。

  • %c: 文字。
  • %d: 整数。
  • %f: float 型の浮動小数点数。
  • %lf: double 型の浮動小数点数。
  • %Lf: long double 型の浮動小数点数。
  • %s: 文字列。
  • %[]: 一致する文字のセットを角かっこで指定します (%[0-9] など)。そのセットに含まれない文字が見つかった場合、マッチングは停止します。

上記のすべてのプレースホルダーのうち、「%c」を除く、先頭の空白文字は自動的に無視されます。 %c は空白文字を無視せず、文字がスペースであるかどうかに関係なく、常に現在の最初の文字を返します。文字の前の空白文字を強制的にスキップしたい場合は、scanf(" %c", &ch) を記述します。つまり、%c の前にスペースを追加して、0 個以上の空白文字をスキップします。

特にプレースホルダー %s について説明しましょう。これは単純に文字列と等価ではありません。その規則は、空白文字 (つまり、スペース、改行文字、タブ文字など) に遭遇するまで、最初の非空白文字から読み取りを開始することです。 %s には空白文字が含まれていないため、複数の %s を一緒に使用しない限り、複数の単語を読み取るために使用できません。これは、scanf() が本や曲のタイトルなど、スペースを含む可能性のある文字列の読み取りには適していないことも意味します。さらに、scanf()%s プレースホルダーに遭遇すると、ヌル文字 \0 が文字列変数の末尾に格納されます。

scanf() が文字列を文字配列に読み取るとき、文字列が配列の長さを超えているかどうかは検出されません。したがって、文字列を格納する場合、配列の境界を超える可能性があり、予期しない結果が発生します。この状況を防ぐには、%s プレースホルダーを使用するときに、読み取り文字列の最大長を指定する必要があります。これは、%[m]s として記述されます。ここで、[m] は整数で、読み取った文字列の最大長を取得し、それ以降の文字は破棄されます。

文字名[11];
scanf("%10s", 名前);

上記の例では、name は長さ 11 の文字配列であり、scanf() のプレースホルダー %10s は、ユーザーが入力した最大 10 文字を読み取ることができ、次の文字が読み込まれることを意味します。破棄されるため、配列オーバーフローの危険性がありません。

割り当て無視文字

場合によっては、ユーザー入力が意図した形式に準拠していない場合があります。

scanf("%d-%d-%d", &year, &month, &day);

上の例では、ユーザーが「2020-01-01」と入力すると、年、月、日が正しく解釈されます。問題は、ユーザーが「2020/01/01」などの他の形式を入力する可能性があることです。この場合、「scanf()」はデータの解析に失敗します。

この状況を回避するために、scanf() は代入抑制文字 * を提供します。プレースホルダーのパーセント記号の後に「*」が追加されている限り、プレースホルダーは値を返さず、解析後に破棄されます。

scanf("%d%*c%d%*c%d", &year, &month, &day);

上記の例では、プレースホルダーのパーセント記号の後に代入無視文字 '*' とともに %*c が追加されており、このプレースホルダーには対応する変数がなく、解釈後に返す必要がないことを示しています。

sscanf()

sscanf() 関数は、scanf() とよく似ていますが、sscanf() はユーザー入力ではなく文字列からデータを取得する点が異なります。そのプロトタイプはヘッダー ファイル stdio.h で定義されます。

int sscanf(const char* s, const char* 形式, ...);

sscanf() の最初のパラメータは、データを取得する文字列ポインタです。他のパラメータはすべて scanf() と同じです。

sscanf() は主に、他の入力関数によって読み取られた文字列を処理し、そこからデータを抽出するために使用されます。

fgets(str, sizeof(str), stdin);
sscanf(str, "%d%d", &i, &j);

上の例では、fgets() はまず標準入力からデータ行を取得し (fgets() の概要については次の章を参照)、それを文字配列 str に格納します。次に、sscanf() は文字列 str から 2 つの整数を抽出し、それらを変数 ij に代入します。

sscanf() の利点の 1 つは、データ ソースがストリーム データで一度しか読み取れない scanf() とは異なり、データ ソースがストリーム データではないため、繰り返し使用できることです。

sscanf() の戻り値は、正常に割り当てられた変数の数です。抽出が失敗した場合は、定数 EOF が返されます。

getchar()、putchar()

(1)getchar()

getchar() 関数は、ユーザーがキーボードから入力した文字を返し、パラメータなしで使用されます。プログラムがこのコマンドに到達すると、一時停止してキーボードからのユーザー入力を待ちます。これは、scanf() メソッドを使用して文字を読み取るのと同じです。そのプロトタイプはヘッダー ファイル stdio.h で定義されます。

チャーch;
ch = getchar();

// と同等
scanf("%c", &ch);

getchar() は先頭の空白文字を無視せず、それがスペースであるかどうかに関係なく、現在読み込まれている最初の文字を常に返します。読み取りが失敗した場合、定数 EOF が返されます。EOF は通常 -1 であるため、戻り値の型は char ではなく int に設定する必要があります。

getchar() は読み込んだ文字を返すので、ループ条件で使用できます。

while (getchar() != '\n')
  ;

上の例では、読み取られた文字が改行文字 (\n) と等しい場合にのみループが終了します。改行文字は、特定の行をスキップするためによく使用されます。 「while」ループのループ本体にはステートメントがありません。これは、この行では操作が実行されないことを意味します。

次の例では、特定の行の文字長を計算します。

int len = 0;
while(getchar() != '\n')
  レン++;

上記の例では、getchar() が文字を読み取るたびに、改行文字が読み込まれるまで長さ変数 len が 1 ずつ増加します。このとき、len はその行の文字長です。

次の例では、スペース文字をスキップします。

while ((ch = getchar()) == ' ')
  ;

上の例では、ループ終了後、変数 ch は最初の非スペース文字と等しくなります。

(2)putchar()

putchar() 関数は引数の文字を画面に出力します。これは、printf() を使用して文字を出力するのと同じです。そのプロトタイプはヘッダー ファイル stdio.h で定義されます。

putchar(ch);
// と同等
printf("%c", ch);

操作が成功した場合、putchar() は出力文字を返し、それ以外の場合は定数 EOF を返します。

(3) まとめ

これら 2 つの関数 getchar()putchar() の使用法は scanf()printf() よりも簡単で、通常はマクロを使用して実装されるため、 scanf() よりも優れています。と printf() の方が高速です。単体キャラクターを操作する場合は、最初にこの2つの機能を使用することをお勧めします。

プット()

puts() 関数は、パラメータ文字列を画面 (標準出力) に表示するために使用され、文字列の末尾に改行文字を自動的に追加します。そのプロトタイプはヘッダー ファイル stdio.h で定義されます。

put("ここにいくつかのメッセージがあります:");
put("Hello World");

上の例では、puts() は画面上に 2 行のコンテンツを出力します。

書き込みが成功すると、p​​uts() は負でない整数を返し、それ以外の場合は定数 EOF を返します。

を取得します()

gets() 関数は、以前は stdin から入力行全体を読み取るために使用されていましたが、現在は廃止されており、ここでは引き続き紹介されています。

この関数は、改行文字が見つかるまで先頭の空白文字をスキップせずにユーザー入力の行を読み取ります。この関数は改行文字を破棄し、残りの文字をパラメータ変数に入れ、これらの文字の末尾にヌル文字 \0 を追加して文字列にします。

puts() と組み合わせてよく使用されます。

文字の単語[81];

Puts("文字列を入力してください");
(単語) を取得します。

上記の例では、puts() を使用して画面にプロンプ​​トを出力し、その後、gets() を使用してユーザー入力を取得します。

gets() によって取得される文字列は文字配列変数の最大長を超える可能性があるため、代わりに fgets() を使用しないことをお勧めします。


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0