Help us understand the problem. What is going on with this article?

C/C++のフォーマット指定子『%[^\n]』とは?? その1: 文法編

1行目からリンクを貼ろうと思いましたが、やっぱやめときます
とあるサイトで見た事が無い記法を発見しました。

こんな記述
scanf("%[^\n]%*c", c);

ほうほう…こうやって書けば…ん?

君は誰だい?
%[^\n]

げっ!何これ!

(アニメのOP前の映像みたいな演出終了)

fscanf()(空白文字)も読み込みたい!!

そう思ってggったんですよ。
そしたらまだ知らなかった方法を2つ発見しました。

  1. strtok()を使用する
  2. フォーマット指定子%[^\n]を使用する
…でもね、僕は知らないわけですよ。%[^\n]を。
勿論ggってみました。
"%[^\n]" - Google 検索
残念!この謎の記法についての情報は(10件中)2件しかありませんでした。
…そういえば、この記事も検索に引っかかんないんですよねぇ。
って事は?これを見っけたあなたは……ラッキーな人?暇人?

フォーマット指定子の復習(一応)

フォーマット指定子といえば%d%c等があります。
%-4.2fなんて書き方も出来ますね。
あっやっぱ知らない人や思い出せない人は、自分で調べて下さい。

取り敢えず実際に使ってみる

※環境は Win10Pro(CORE i7 8th Gen + UHD620) + VS2017(2020年4月時点で最新)
先ずは普通に標準入力から読み込みをしてみましょう。

main.c
#pragma warning(disable:4996)
#include <stdio.h>
#include <Windows.h> //system() を使用する為

int main(void) {
    char str[64] = { '\0' };
    scanf("%s", str); //読み込み
    printf("str = \"%s\"\n", str); //出力
    system("pause");
}
実行結果
Ashole is anus
str = "Ashole"
続行するには何かキーを押してください . . .

※以降はプリプロセッサ命令を省略します
ああ。無情。皆さんご存知の通り、空白文字が区切りになって、"is"以降が読み込まれません…。
では早速、あの秘密兵器を使ってみましょう。

%[^\n]を使用したmain.c
int main(void) {
    char str[64] = { '\0' };
    scanf("%[^\n]%s", str); //読み込み
    printf("str = \"%s\"\n", str); //出力
    system("pause");
}
実行結果
Ashole is Anus
nanigaokottanoda
(!!メモリ書き込みアクセス違反!!)

※以降はscanf以外も省略します
あれ?うまくいきませんでした。2回改行した時点でメモリアクセス違反で落ちました。

%sを消したmain.c
    scanf("%[^\n]", str); //読み込み
実行結果
Anus is Anus
str = "Anus is Anus"
続行するには何かキーを押してください . . .

今度は上手くいきました。改行文字\nまで読み込まれています。

%[^\n]sにしたmain.c
    scanf("%[^\n]s", str); //読み込み

これでも上手くいきました。

%[^\ns]にしたmain.c
    scanf("%[^\ns]", str); //読み込み
実行結果
Anus is My father
str = "Anu"
続行するには何かキーを押してください . . .

しかし、sを四角括弧[ ]の中に押し込んだら、1つ目のsの直前迄しか読み込まれませんでした。
まるでsが区切り文字になったみたいですね。


……もう勘の良い人なら何かに気付いたと思います。

そうだネ(魚を捌くサイコパス系動画投稿者)

%[^ ... ]内に記述された文字が区切り文字になる

(この解釈で合っていると思います。)
なので、

%[^\n]を使用したmain.c
    scanf("%[^\n]%s", str); //読み込み

と記述すれば

  1. 設定されている\nまで読み込む
  2. 区切り文字まで読み込む
と、文字配列が2回読み込まれたので、メモリ書き込みアクセス違反が起こっていたって訳ですね!
いや基本を押さえてれば誰でも分かるわ!
そして、
%[^\ns]にしたmain.c
    scanf("%[^\ns]", str); //読み込み

これでsが区切り文字の様に振る舞っていたのは、本当に区切り文字になっていたからだったのです!

他の文字で試してみる

^を区切り文字にする場合は、

'^'を区切り文字にするぜ!なmain.c
    scanf("%[^^]", str); //読み込み
実行結果
Anus aaagghhhhhhhhh!!!!
anus!anus
aaaa!!^
str = "Anus aaagghhhhhhhhh!!!!
anus!anus
aaaa!!"
続行するには何かキーを押してください . . .

こういう風に記述します。なんと!改行文字も読み込まれました。


これへ続く…
(こんなの使わねぇ!って人と、只々これを使えるようになるだけで満足なおチビちゃんは、次以降は読まなくても大丈夫だと思います。)

参考…?

こちらは Java の正規表現:文字クラスです。見ると分かりますが、似てますよね。
なんと、確認したら[abc], [^abc], [a-z](※環境依存)は動作しました。(それ以外は仕様に無い様です)。








Sankyuri
気紛れでYouTubeに動画を投稿しているアマチュアプログラマです Twitter ID:Sankyuri
https://www.youtube.com/channel/UC05zgpVRFyGXRvN5LC98Hfg/featured
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした