内容に誤りや勘違いあるのでコメント欄も見てください(2022/03/14追記)
はじめに
こんにちは、麻菜結です。最近はC言語の勉強に力を入れているのですが、正直標準入力の格納がめんどくさすぎて「Pythonならmapでいっぱつなのになぁ」とか思いながら毎回検索していました。よく考えたら一回調べたらQiitaにまとめたらいいじゃんと思ったのでまとめようと思います。
この記事では、競技プログラミングでありがちなこういうやつ
H W
e11 e12 ... e1W
e21 e22 ... e2W
...
eH1 eH2 ... eHW
をどう読み込むかなみたいな話をしていきます。まず使いまわせる関数を定義しまして、その後具体的にこのようにするみたいな構成で行こうとおもいます。よろしくお願い致します。環境は以下の通りです。
OS:
Windows10
コンパイラ:
Borland C++ Compiler
Embarcadero C++ 7.40 for Win32 Copyright (c) 1993-2017 Embarcadero Technologies, Inc.
Preliminary version built Aug 23 2019 08:48:10
Revision 7.40.7174.37042
一行を読む関数
ここでは、一行を読み込む関数を紹介いたします。そんなんscanf
で良いじゃんと思われるかもしれませんしある程度はその通りなのですが、scanf
ではこの関数だけで標準入力を全て奪うみたいな動作をするので繰り返し呼び出して行を読み込むみたいな動作ができません(断言するの怖い、間違っていたら教えて)。そのためfgets
を用いるようにします。以下、ソースコードです。
#include <stdio.h>
#include <string.h>
#define LEN_BUFFER 255
void readLine(char buf[LEN_BUFFER]){
fgets(buf, LEN_BUFFER, stdin);
buf[strlen(buf)] = '\0';
}
int main(){
char buf[LEN_BUFFER];
readLine(buf);
printf("%s", buf);
return 0;
}
readLine
関数は、char配列を受け取って標準入力を入れる関数です。fgets
は改行文字込みの読込になりますが、配列読み込みでは必要ないのでstring.h
のstrlen
を用いて配列の末尾を特定し、ヌル文字を挿入しています。
文字列を数値の配列に変換する関数
続いて標準入力から持ってきた文字列を数値の配列に変換する関数です。とりあえずソースコードを見てください。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN_BUFFER 255
#define LEN_ARRAY 10
void readLine(char buf[LEN_BUFFER]){
fgets(buf, LEN_BUFFER, stdin);
buf[strlen(buf)] = '\0';
}
void readArray(char raw[LEN_BUFFER], int ary[LEN_ARRAY]){
int i;
char *s;
s = strtok(raw, " ");
for(i=0; s; s=strtok(NULL, " ")){
ary[i++] = atoi(s);
}
}
int main(){
int ary[LEN_ARRAY], i;
char buf[LEN_BUFFER];
readLine(buf);
readArray(buf, ary);
for(i = 0; i < 4; i++){
printf("%d\n", ary[i]);
}
return 0;
}
strtok
関数はstring.h
に含まれる、文字列を分割する関数です。定義は以下の通り
#include <string.h>
char *strtok(char *s1, const char *s2);
一度目の呼び出しで文字列の初期化をした後はs1
をNULL
で指定すると初期化した文字から切り取ることができます。なお破壊的な関数なので壊れても良い配列でなければいけません。
高さと幅が与えられた入力を読み込む
例えばこういうものです。
N人の人に好きな数字をM個言ってもらいました。
表として見やすくまとめようと思うので数値を[100]のように装飾し出力して下さい。
人ごとに改行してください。
N M
d11 d12 ... d1M
...
dN1 dN2 ... dNM
問題の制限を以下に示す。
1 <= N < 100
1 <= M < 10
dは整数である。
このような、縦と横が与えられてそれを扱う問題を考えます。テストケースは以下のモノで
3 4
0 -1 2 -3
-4 5 -6 7
8 -9 10 -11
このように出力されればよい訳ですね。
[0][-1][2][-3]
[-4][5][-6][7]
[8][-9][10][-11]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN_BUFFER 255
#define LEN_ARRAY 10
#define HEIGHT 100
void readLine(char buf[LEN_BUFFER]){
fgets(buf, LEN_BUFFER, stdin);
buf[strlen(buf)] = '\0';
}
void readArray(char raw[LEN_BUFFER], int ary[LEN_ARRAY]){
int i;
char *s;
s = strtok(raw, " ");
for(i=0;s; s=strtok(NULL, " ")){
ary[i++] = atoi(s);
}
}
int main(){
int N, M, ary[HEIGHT][LEN_ARRAY], i, j;
char buf[LEN_BUFFER];
readLine(buf);
readArray(buf, ary[0]);
N = ary[0][0];
M = ary[0][1];
for(i = 0; i < N; i++){
readLine(buf);
readArray(buf, ary[i]);
for(j = 0; j < M; j++){
printf("[%d]", ary[i][j]);
}
printf("\n");
}
return 0;
}
今さらですけど、新しめのCコンパイラでは、引数にまるで配列を指定できるような書き方が出来ます。もちろん内部ではポインタを渡しているのですが、数値を書くことで結構しっかり型として扱うことが出来ます。マジックナンバーを避けるため本ソースコードではdefine
して使っていますが競プロではいらないかもしれませんね。もし対応したコンパイラならポインタを渡すよりわかりやすいのでお勧めです。
おわりに
おつかれさまです。最近は忙しくて記事として書きたい奴が山積していてtodoがすごいことになっています。どうしよう、我ながら大変な記事ならこつこつ書けば良いのにこういうさっと書けそうなのやるんじゃなくてと思うのですが…まあ頑張ります。
ここまで読んでくださってありがとうございます、お役に立てたならば幸いです。