プログラミングコンテストとかの標準入力の受け取り方メモ（C言語）


プログラミングコンテストとかそういう諸々で標準入力の受け取り方をよく忘れるため、頻繁に使うものをメモしておきます。

paizaラーニングさんの問題集をベースとして一覧化しています。

https://paiza.jp/works/mondai/stdin/problem_index?language_uid=c

# 1つのデータの入力

> 文字列Sが1行で入力されます。
> ・Sは1文字以上100文字以下の文字列
> ・Sの各文字は英小文字または大文字または数字

## 入力例
```text
abc
```

```text
1242
```

## 取得例
```C
#include <stdio.h>
int main(void){
    char buf[100 + 1];
    fgets(buf, sizeof(buf), stdin);
    printf("%s\n", buf);
    return 0;
}
```

文字列Sが数値なのであれば、`sscanf(buf, "%d", num);`で変換します。

# 1行のデータの入力

> 文字列Sが1行で入力されます。
> ・Sは1文字以上100文字以下の文字列
> ・Sの各文字は英小文字または大文字または数字または半角スペース

## 入力例
```text
i am alice
```

```text
3000
```

## 取得例
```C
#include <stdio.h>
int main(void){
    char buf[100 + 1];
    fgets(buf, sizeof(buf), stdin);
    printf("%s\n", buf);
    return 0;
}
```

# 3行のデータの入力

> 文字列A, B, Cが各行で入力されます。
> ・A, B, Cは1文字以上100文字以下の文字列
> ・A, B, Cの各文字は英小文字または大文字または数字または半角スペース

## 入力例
```text
alice
bob
curl
```

```text
this is a pen
that is a watch
and so on
```

## 取得例
```C
#include <stdio.h>
int main(void){
    char buf[100 + 1];
    
    for(int i = 0; i < 3; i++) {
        fgets(buf, sizeof(buf), stdin);
        printf("%s", buf);
    }
    return 0;
}
```

行毎でデータを取得すればよい場合は、楽に処理できました。

# N行のデータの入力

> 1行目でNが与えらます。
> 続くN行の各行で文字列が与えられます。
> ・1 <= N <= 10
> ・入力される各文字列は1文字以上100文字以下の文字列
> ・入力される各文字列の各文字は英小文字または大文字または数字または半角スペース

## 入力例
```text
3
good bye
good morning
good evening
```

```text
4
good
bad
soso
umm
```

## 取得例
```C
#include <stdio.h>
int main(void){
    char buf[100 + 1];
    int num;
    
    fgets(buf, sizeof(buf), stdin);
    sscanf(buf, "%d", &num);
    
    for(int i = 0; i < num; i++) {
        fgets(buf, sizeof(buf), stdin);
        printf("%s", buf);
    }
    return 0;
}
```

カウンタ変数に取得した回数を反映させればよいだけなので簡単。

# 3つのデータの入力

> 3つの文字列が半角スペース区切りで1行で与えれます。
> ・入力される各文字列は1文字以上100文字以下の文字列
> ・入力される各文字列の各文字は英小文字または大文字または数字

## 入力例
```text
hello hey oi
```

```text
1 2 3
```

## 取得例
```C
#include <string.h>
#include <stdio.h>
int main(void){
    char buf[1000];
    fgets(buf, sizeof(buf), stdin);
    
    char *token;
    int i = 0;
    token = strtok(buf, " ");
    while(token != NULL && i < 3) {
        printf("%s\n", token);
        token = strtok(NULL, " ");
        i++;
    }
    return 0;
}
```

1行に複数データが格納されている場合は多少厄介というか、調べて出てきた方策をとっています。
`strtok()`って初めて使いました。

どうやら、strtokはグローバルな状態を保持して、処理をしていくため、複数の文字列に対してstrtokで処理を行うことはできないようです。

たとえば、以下は望んだ動作をしません。

<details><summary>不適切なコード</summary>

```C
#include <string.h>
#include <stdio.h>
int main(void){
    char buf1[1000], buf2[1000];
    fgets(buf1, sizeof(buf1), stdin);
    fgets(buf2, sizeof(buf2), stdin);
    
    char *token1, *token2;
    int i = 0;
    token1 = strtok(buf1, " ");
    token2 = strtok(buf2, " ");
    while(token1 != NULL && i < 3) {
        printf("%s\n", token1);
        printf("%s\n", token2);
        token1 = strtok(NULL, " ");
        token2 = strtok(NULL, " ");
        i++;
    }
    return 0;
}
```
</details>

この場合は、`strtok_r()`を使ってね、ということらしい。

<details><summary>正常に動いた気がするコード</summary>

入力は、
```text
a b c
d e f
```
となっていて、"adbecf"と出力されます。

```C
#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include <stdio.h>

void remove_newline(char *str) {
    char *p = strchr(str, '\n');
    if (p) {
        *p = '\0';
    }
}


int main(void){
    char buf1[1000], buf2[1000];
    fgets(buf1, sizeof(buf1), stdin);
    fgets(buf2, sizeof(buf2), stdin);
    
    remove_newline(buf1);
    remove_newline(buf2);
    
    char *token1, *token2;
    char *save1 = NULL, *save2 = NULL;
    int i = 0;
    token1 = strtok_r(buf1, " ", &save1);
    token2 = strtok_r(buf2, " ", &save2);
    while(token1 != NULL && token2 != NULL && i < 3) {
        printf("%s", token1);
        printf("%s", token2);
        token1 = strtok_r(NULL, " ", &save1);
        token2 = strtok_r(NULL, " ", &save2);
        i++;
    }
    return 0;
}
```
</details>


# N個のデータの入力

> 1行目でNが与えらます。
> 2行目でN個の文字列が半角スペース区切りで与えれます。
> ・1 <= N <= 10
> ・入力される各文字列は1文字以上100文字以下の文字列
> ・入力される各文字列の各文字は英小文字または大文字または数字

## 入力例
```text
3
hello hey hi
```

```text
1
moo
```

## 取得例

```C
#include <stdio.h>
#include <string.h>
int main(void){
    char buf[100 + 1];
    int num;
    fgets(buf, sizeof(buf), stdin);
    sscanf(buf, "%d", &num);
    
    fgets(buf, sizeof(buf), stdin);
    
    char *token = strtok(buf, " ");
    
    for(int i = 0; i < num; i++) {
        printf("%s\n", token);
        token = strtok(NULL, " ");
    }
    return 0;
}
```

## カンマ区切りの3つのデータの入力

> 3つの文字列がカンマ区切りで1行で与えられます。
> ・入力される各文字列は1文字以上100文字以下の文字列
> ・入力される各文字列の各文字は英小文字または大文字または数字

## 入力例
```text
abc def ghi
```

```text
352 fdg 453
```

## 取得例
```C
#include <stdio.h>
#include <string.h>
int main(void){
    char buf[100 + 1 * 3];
    fgets(buf, sizeof(buf), stdin);
    
    char *token = strtok(buf, ",");
    int i = 0;
    while(token != NULL && i < 3) {
        printf("%s\n", token);
        token = strtok(NULL, ",");
        i++;
    }
    return 0;
}
```

# カンマ区切りのN個のデータの入力

1行目でNが与えらます。
2行目でN個の文字列がカンマ区切りで与えれます。

## 入力例
```text
3
hello,bob,alice
```

```text
5
1 2 alice hello goodbye
```

## 取得例
```C
#include <stdio.h>
#include <string.h>
int main(void){
    char buf[100 + 1 * 3];
    int num;
    
    fgets(buf, sizeof(buf), stdin);
    sscanf(buf, "%d", &num);
    
    fgets(buf, sizeof(buf), stdin);

    char *token = strtok(buf, ",");
    int i = 0;
    while(token != NULL && i < num) {
        printf("%s\n", token);
        token = strtok(NULL, ",");
        i++;
    }
    return 0;
}
```

# 終わりに

各入力条件の記載は原文ままとなります。
（どうでもいいレベルの誤字脱字がありましたね）

ひとまず、これくらいあれば、簡単な問題であれば入力には困らなそうです。

これから何度も書いていって、少しずつ身につけていきたいと思います。