質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.31%
C

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

Q&A

解決済

4回答

3982閲覧

Cでscanf("%d\n",&N)と記載することについて質問です

gregolio122

総合スコア14

C

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

0グッド

3クリップ

投稿2021/09/12 04:58

以下の通りの入力が与えられたとします。

数字1文字(以降Nと呼びます)
aiueo

※数字1文字(N)に関しては、必ず1~5までの範囲が入力されるとします。

ここで、

4
aiueo

上記のような入力がされた場合、期待される出力は以下の通りです。

aiue

さて、そのプログラムを実装するために以下のようなコードを書いてみました。

C

1#include<stdio.h> 2int main(void){ 3 int N,i,dummy; 4 char S[5]; 5//aiueoをS[0]~S[N-1]に入力させる処理 6 scanf("%d",&N); 7//scanf("%c",&dummy); //改行コード読み飛ばし用 8 for(i=0;i<N;i++){ 9 scanf("%c",&S[i]); 10 } 11//S[0]~S[4]を表示する処理。 12 for(i=0;i<N;i++){ 13 printf("%c",S[i]); 14 } 15}

上記コードの実行結果ですが、出力は以下のようになりました。

aiu

おそらく、S[0]に改行コードが意図せず入力されてしまったことが原因だと考えています。
そこで、改行コードがS[0]に入力されるのを回避するために以下の書き方に変えてみました。

C

1#include<stdio.h> 2int main(void){ 3 int N,i,dummy; 4 char S[5]; 5//aiueoをS[0]~S[N-1]に入力させる処理 6 scanf("%d",&N); 7 scanf("%c",&dummy); //改行コード読み飛ばし用 8 for(i=0;i<N;i++){ 9 scanf("%c",&S[i]); 10 } 11//S[0]~S[N-1]を表示する処理。 12 for(i=0;i<N;i++){ 13 printf("%c",S[i]); 14 } 15}

このように、改行コード読み飛ばし処理を追記することで、期待通り、

aiue

と出力されました。

この過程で色々と試してみたのですが、改行コード読み飛ばし処理を記述しなくても以下のような書き方でも期待通りの出力がされることが確認できました。

C

1#include<stdio.h> 2int main(void){ 3 int N,i,dummy; 4 char S[5]; 5//aiueoをS[0]~S[N-1]に入力させる処理 6 scanf("%d\n",&N); //'\n'を追記してみました 7 for(i=0;i<N;i++){ 8 scanf("%c",&S[i]); 9 } 10//S[0]~S[N-1]を表示する処理。 11 for(i=0;i<N;i++){ 12 printf("%c",S[i]); 13 } 14}

これは一体どういうことなのでしょうか?
scanf(%d\n,ほげほげ)といった書き方でも改行コードをスキップして入力取得することが出来るということなのでしょうか?

また、余談なのですが(記憶が曖昧で以下の表記が正しいかはわかりませんが)
scanf("%[0-z],%hoge);
といったようなscanfの書き方があったかと思います。
これについて復習をしたいのですが、なんとか演算子、といった名前だったような気がするのですが思い出せません。
お心当たりがあれば、この書き方の名称を教えていただけますと幸いです。

以上、よろしくお願いします。

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

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

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

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

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

guest

回答4

0

scanfの書式指定は、「入力文字並び(Cの"文字列"と区別する意味で敢えて文字並びと呼んでいます。ストリームと呼んだ方がいいかな...)を順次調べて、書式指定に一致/合致しているところまで取り込む。一致しない入力は、入力文字並びに残してscanfを終了する」という働きをします。
なので、
scanf("%d\n",&N);
に対して例えば3[改行]と入力したなら、"0個以上のホワイトスペースに引き続き、整数として解釈できる文字並び[改行]"と一致するので、そこまでを入力から取り込み、%dの部分に一致する"3"を整数に変換して&Nで与えられたアドレスに格納する、'\n'は入力から取り込まれるが捨てられる、ということをしています。

ということで質問内容に一応答えた上で脱線(の方が主文になったり)。

ちょっと難しく考えすぎていないでしょうか。もっと単純に

char S[6];//要素数には終端文字の'\0'分が必要 //略 scanf("%d",&N); scanf("%s",S); for(int i=0;i<N;i++){ printf("%c",S[i]); }

で良かったのでは。別に入力だけは全部受け取ったって、表示する文字数を制御すれば「出力」は期待通りに得られるのですから。

さらには、printfのほうの書式指定には桁数や精度のところを*で書くと後の引数から数値を拾ってきて当てはめるという機能がある、なんていうことを知っていると、

scanf("%d",&N); scanf("%s",S); printf("%.*s",N,S[i]); //*の部分にNの値がはまる

でも期待する結果が得られたりします。

投稿2021/09/12 11:06

編集2021/09/12 11:09
thkana

総合スコア7733

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

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

gregolio122

2021/09/14 17:18

\ndで改行コードが読み込まれるが、それは捨てられる(Scanfで格納先の変数には残らない)ということなのですね。 また、スキャン集合指定子についても教えてくださりありがとうございまし! 大変勉強になりましたし、本件とは別ですがスキャン演算子について復習をしたいと思います。
guest

0

ベストアンサー

詳しい回答は既にある通りで、scanfは、標準入力ストリームからフォーマットに一致した部分だけを読み取り、そうでない部分はそのまま残すので、
%dだけで受け取ると、一致した数字の部分だけ受け取って、その後に続く改行はストリームにそのまま残されます。

それでも、その次が%s%d等であれば、これらは先頭にある空白文字(改行を含む)を読み飛ばしてくれるので特に問題は無いのですが、
%cだけは、空白文字も含めて受け取ってしまう為、残された前回の改行も読み取ってしまいます。

それを回避する為に、%d\nとして、改行までを一致させて、先に消化させているのです。

他には、 %c(前にスペースを付ける)でも、空白文字を読み飛ばしてくれたと思います。

もしくは、1行分丸ごと読み取ってから、sscanfで処理をするとか。

%[a-z]の書き方の名前は、スキャン集合指定子です。

投稿2021/09/12 06:54

amiya

総合スコア1218

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

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

0

実験1

実験してみましょう。

C

1#include <stdio.h> 2 3int main(void) { 4 char buffer[256]; 5 char ch; 6 7 scanf("%s", buffer); 8 scanf("%c", &ch); 9 printf("%s[%c:0x%02X]\n", buffer, ch, ch); 10 11 return 0; 12}

hoge[改行] の順で入力します。

stdout

1hoge[ 2:0x0A]

hoge[スペース][改行] の順で入力します。

stdout

1hoge[ :0x20]

実験2

さて、変換文字列を %s\n にしたときはどうなるか?
Man page of SCANFには次のように記載されています。

ホワイトスペース (スペース、タブ、改行など; isspace(3) 参照) の列。 この命令は、入力中の任意の個数のホワイトスペースに一致する。 (「何もなし」にも一致する)。

実験してみましょう。

C

1#include <stdio.h> 2 3int main(void) { 4 char buffer[256]; 5 char ch; 6 7 scanf("%s\n", buffer); // "%s " や "%s\t" でも良い 8 scanf("%c", &ch); 9 printf("%s[%c:0x%02X]\n", buffer, ch, ch); 10 11 return 0; 12}

hoge[改行]a の順で入力します。

stdout

1hoge[a:0x61]

hoge[スペース][改行][スペース]a の順で入力します。

stdout

1hoge[a:0x61]

任意長の空白文字類が読み飛ばされていますね。

なんとか演算子

scanf("%[0-z],%hoge);

といったようなscanfの書き方があったかと思います。

scanf("%[a-z]s", buffer) ですかね。
これはaからzの文字列を読み取ります。(文字コードが連続する環境であれば)

C

1scanf("%[a-z]s", buffer); 2scanf("%c", &ch); 3printf("%s[%c:0x%02X]\n", buffer, ch, ch);

例えば abc123ABC[改行] と入力すると、次のような結果になります。

stdout

1abc[1:0x31]

[0-z] と書くことはあまり多くないんじゃないかと思います。
: や ; などを読み込むことはおそらく期待していないので。[0-9A-Za-z] の方が良いかも。

これについて復習をしたいのですが、なんとか演算子、といった名前だったような気がするのですが思い出せません。

お心当たりがあれば、この書き方の名称を教えていただけますと幸いです。

すみません、名称については心当たりありません。
ただ、○○演算子とはなかなか呼ばないように思います。

投稿2021/09/12 05:53

編集2021/09/12 06:01
LouiS0616

総合スコア35676

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

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

0

まず、

char S[5];

Sを初期化せずに上書きしてるだけなので、文字列終端の'\0'がはいりません
また、最初に5を入力されると文字列として破綻するんで、もうちょいこの数字を大きくしましょう
そこらへん修正してやり直してみてください

#単に最初にSを0クリアするだけでいいかと

投稿2021/09/12 05:12

編集2021/09/12 05:13
y_waiwai

総合スコア88163

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

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

LouiS0616

2021/09/12 12:17 編集

> the next pointer must be a pointer to the initial element of a character array that is long enough to hold the input sequence and the terminating null byte ('\0'), which is added automatically. https://linuxjm.osdn.jp/html/LDP_man-pages/man3/scanf.3.html > Always stores a null character in addition to the characters matched (so the argument array must have room for at least width+1 characters) https://en.cppreference.com/w/c/io/fscanf --- Sの要素数を増やすべきという意見については、尤もだと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問