C言語でPython風input関数
C言語のscanf
関数は、競技プログラミングなど入力が正確な場合は非常に便利ですが、人間による入力が絡む場合や、違った型で複数回の入力をする時にはなるべく避けたいものです。
というわけで、Pythonに存在する便利な関数input
をC言語で再現してみました。
Pythonのinput関数
Pythonにあるinput
関数は以下のように使います。
a = int(input("a : ")) b = int(input("b : ")) print(f"{a} + {b} = {a+b}")
実行例
a : 5 b : 10 5 + 10 = 15
input
関数は受け取った引数をプロンプトとして表示し、改行までの入力を受け取って文字列として返すというものです。
上記のプログラムでは受け取った文字列をint
型にキャストして計算し、表示しています。
C言語で真似てみる
C言語でも、プロンプト内容(ディスクリプションとし、引数名disc
としています。)を受け取り、標準入力で受け取った文字列のポインタを返す関数として実装すればいいわけです。
input.c
#include <stdio.h> #include <string.h> #include <stdarg.h> #ifndef _MEM_ #define _MEM_ 256 #endif char _input_str[_MEM_][_MEM_]; int _input_str_index = 0; char* input(char *disc, ...) { if (_input_str_index == _MEM_) { printf("!!coution!! : buffers are lacked!\n"); // return NULL; _input_str_index = 0; // 古いものから上書き } va_list args; va_start(args, disc); vprintf(disc, args); va_end(args); fgets(_input_str[_input_str_index], _MEM_, stdin); // '\n'削除 int len = strlen(_input_str[_input_str_index]); if (_input_str[_input_str_index][len-1] == '\n') { _input_str[_input_str_index][len-1] = '\0'; } return _input_str[_input_str_index++]; }
予め、_MEM_
個分、_MEM_
文字配列を二次配列_input_str
として確保しています。1
本当はmalloc
関数で逐次メモリを確保するようにしたかったのですが、free
関数を外側で呼ぶ必要があるのは嫌だったので、予め確保するというスタイルを取ってみました。
_input_str_index
によって入力された文字列の連番を管理しています。_MEM_
個以上の入力があった場合の例外処理ですが、とりあえず思いつかなかったので警告を出したうえで上書きする仕様としました。
肝心の標準入力はfgets
関数で行っています。fgets
関数は第一引数に保存用の文字列のポインタ、第二引数に確保されている文字列長(要は最大文字数)、第三引数に読み込むファイルポインタ(今回は標準入力なのでstdin
)を指定する関数です。
scanf
と異なり、最大文字数以上は切り捨てられるのでバッファオーバーランの危険性がありませんし、改行まできっちり受け取るのでストリーム上(標準入力上)に変なものが残らず安全です。
逆に前述の通り改行まで受け取ってしまっているので、その後ろの部分にて改行は削除しています。その後、受け取った文字列が入ってるポインタを返り値として返しています。
stdarg.h
周りのva_list
などは、printf
関数のようにフォーマット機能を使用するために使っています。stdarg.h
を読み込むと、引数に...
と指定することで可変長引数を受け取ることができるようになります。その後、vprintf
関数を使用することで、disc
文字列にフォーマット機能で可変長引数を代入して表示できるようにしました。この辺りは蛇足的機能なので理解できなくても大丈夫です。(僕もよくわかってない...(^ ^; )
さらに、input.c
を読み込むためにinput.h
も用意しておきます。
#ifndef _INCLUDE_input_h_ #define _INCLUDE_input_h_ extern char* input(char *disc, ...); #endif
input.c
を使用するときは、例えばmain
関数が入ったソースコードと同じ階層にinput.h
、input.c
を置き、$ gcc test.c input.c
としてコンパイルしましょう。
使用例
例えば最初のPythonプログラムはC言語で以下のように書けます。
#include <stdio.h> #include <stdlib.h> #include "input.h" int main(void) { int a = atoi(input("a : ")), b = atoi(input("b : ")); printf("%d + %d = %d\n", a, b, a+b); return 0; }
a : 5 b : 10 5 + 10 = 15
Pythonとほとんど変わらない書き方で同じ機能を実現できました!よきよき
さらにせっかく実装したフォーマット文字列も使用してみましょう。
#include <stdio.h> #include <stdlib.h> #include "input.h" int main(void) { int sum = 0, tmp = 1; while (tmp != 0) { tmp = atoi(input("%2d += ", sum)); sum += tmp; } return 0; }
実行例
0 += 1 1 += 2 3 += 3 6 += 4 10 += 5 15 += 6 21 += 7 28 += 8 36 += 9 45 += 10 55 += 0
フォーマット文字列機能によりprintf
関数みたいにプロンプト内容を自由に指定することができます。2
これで忌々しいscanf
関数を使わずに楽に標準入力を受け取れますね!
この記事が参考になれば幸いです。ではまたいつか