なぜprintfで先行するのではなく、末尾の改行を使用するのですか?


79

を使用するときは、先頭の改行を避けるべきだと聞きましたprintf。そのため、代わりにprintf("\nHello World!")使用する必要がありますprintf("Hello World!\n")

上記のこの特定の例では、出力が異なるため意味がありませんが、これを考慮してください。

printf("Initializing");
init();
printf("\nProcessing");
process_data();
printf("\nExiting");

に比べ:

printf("Initializing\n");
init();
printf("Processing\n");
process_data();
printf("Exiting");

見栄えが良いことを除けば、後続の改行を使用しても利点はありません。他の理由はありますか?

編集:

ここで、そして今、投票権について話します。この質問は主に設計に関するものであるため、これはStack overflowに属するとは思わない また、この問題に対する意見かもしれませんが、Kilian Fothの回答cmasterの回答は、1つのアプローチで非常に客観的な利点があることを証明しています。


5
この質問は、「コードの問題」(トピック外)と「概念的なソフトウェア設計」(トピック上)の境界にあります。閉じられる可能性がありますが、あまり難しく考えないでください。それでも、具体的なコード例を追加することは正しい選択だと思います。
キリアンフォス

46
最後の行は、末尾の改行なしでLinuxのコマンドプロンプトとマージされます。
GrandmasterB

4
「見た目が良く」、マイナス面がない場合は、それを行う十分な理由です、IMO。優れたコードを書くことは、優れた小説や優れた技術論文を書くことと同じです-悪魔は常に細部に宿っています。
alephzero

5
やるinit()process_data()何でも自分自身を印刷?そうした場合、結果はどのようになると思いますか?
ベルギ

9
\nは行区切り文字ではなく、行終端文字です。これは、UNIX上のテキストファイルのほとんどがで終わるという事実によって証明されます\n
ジョナサンラインハート

回答:


222

かなりの量の端末I / Oがラインバッファリングされているため、メッセージを\ nで終了することにより、確実にタイムリーに表示されます。先頭に\ nが付いている場合、メッセージは一度に表示される場合とされない場合があります。多くの場合、これは各ステップが前のステップの進行メッセージを表示することを意味します。これにより、プログラムの動作を理解しようとすると混乱や時間の無駄がなくなります。


20
これは、クラッシュするプログラムのデバッグにprintfを使用する場合に特に重要です。改行をprintfの最後に配置すると、コンソールへの標準出力が各printfでフラッシュされます。(stdoutがファイルにリダイレクトされる場合、stdライブラリは通常行バッファリングではなくブロックバッファリングを行うため、最後に改行があってもprintfデバッグがクラッシュするのは非常に困難です。)
Erik Eidt

25
@ErikEidt fprintf(STDERR, …)代わりに使用する必要があることに注意してください。これは通常、診断出力用にまったくバッファリングされません。
デュプリケータ

4
@Deduplicator診断メッセージをエラーストリームに書き込むことには欠点もあります。多くのスクリプトは、エラーストリームに何かが書き込まれた場合にプログラムが失敗したと想定します。
Voo

54
@Voo:stderrへの書き込みを想定しているプログラムは、失敗自体が正しくないことを示していると主張します。プロセスの終了コードは、失敗したかどうかを示すものです。失敗した場合、stderrの出力に理由が説明されます。プロセスが正常に終了した場合(終了コード0)、stderr出力は、特定のマシン解析可能なセマンティック(たとえば、人間が読み取れる警告を含む場合があります)を持たない人間のユーザーの情報出力と見なされる必要がありますが、stdoutは実際の出力ですおそらくさらなる処理に適したプログラム。
ダニエル・プライデン

23
@Voo:どのプログラムを説明していますか?あなたが説明したように振る舞う、広く使用されているソフトウェアパッケージは知りません。実行するプログラムがあることは知っていますが、上記の規則を作成しただけではありません。それは、UnixまたはUnixに似た環境のプログラムの大部分が機能する方法であり、私の知る限り、プログラムの大部分は常に持っています。スクリプトがうまく処理できないという理由だけで、stderrへの書き込みを回避するプログラムを推奨することはありません。
ダニエルプライデン

73

POSIXシステム(基本的には、Linux、BSD、オープンソースベースのシステム)で、行は改行で終了する文字列として定義されます\n。これは、すべての標準のコマンドラインツールは、(これらに限定されない)を含む、上に構築基本的な前提であるwcgrepsedawk、とvim。これは、一部のエディター(などvim)が常に\nファイルの最後に追加する理由であり、以前のC標準ではヘッダーが\n文字で終わる必要があった理由でもあります。

Btw:\n行を終了すると、テキストの処理がはるかに簡単になります。ターミネータを取得すると、完全な行が確実に取得されます。そして、そのターミネーターにまだ出会っていない場合は、より多くのキャラクターを見る必要があることは確かです。

もちろん、これはプログラムの入力側にありますが、プログラム出力はプログラム入力として頻繁に使用されます。そのため、他のプログラムへのシームレスな入力を可能にするために、出力は規則に従う必要があります。


25
これは、ソフトウェアエンジニアリングにおける最も古い議論の1つです。改行(または、プログラミング言語では、セミコロンのような別の「ステートメントの終わり」マーカー)を行終端記号または行区切り記号として使用する方が良いでしょうか?どちらのアプローチにも長所と短所があります。Windowsの世界では、改行シーケンス(通常はそこにあるCR LF)が行区切り文字であるという考えにほぼ決まっているため、ファイルの最後の行はそれで終わる必要はありません。しかし、Unixの世界では、改行シーケンス(LF)は行末記号であり、多くのプログラムはこの仮定に基づいて構築されています。
ダニエル・プライデン

33
POSIX 、行を「ゼロ以上の非改行文字と終了改行文字のシーケンス」
パイプ

6
与えられた@pipeが言うように、それはPOSIX仕様でだと、我々はおそらくそれを呼び出すことができますデジュールとは対照的に、事実上の答えが示唆するように?
Baldrickk

4
@Baldrickkそうです。私は今、答えをより肯定的なものに更新しました。
cmaster

また、Cはソースファイルに対してこの規則を作成します。改行で終わらない空でないソースファイルは、未定義の動作を引き起こします。
R ..

31

他の人が述べたことに加えて、もっと単純な理由があるように感じます:それが標準だからです。何かがSTDOUTに出力されるときはいつでも、それはほとんど常にそれがすでに新しい行にあると仮定するので、新しいものを始める必要はありません。また、書き込まれる次の行が同じように動作することを前提としているため、新しい行を開始することで終了します。

標準の末尾の改行行でインターリーブされた先頭の改行行を出力すると、次のようになります。

Trailing-newline-line
Trailing-newline-line

Leading-newline-line
Leading-newline-line
Leading-newline-lineTrailing-newline-line
Trailing-newline-line

Leading-newline-lineTrailing-newline-line
Trailing-newline-line
Trailing-newline-line

...これはおそらくあなたが望むものではありません。

コードで先頭の改行のみを使用し、IDEでのみ実行する場合、大丈夫になることがあります。端末で実行するか、コードと一緒にSTDOUTに書き込む他の人のコードを導入すると、すぐに上記のような望ましくない出力が表示されます。


2
対話型シェルで中断されたプログラムでも同様のことが起こります-部分的な行が印刷される(改行がない)と、シェルはカーソルがどの列にあるかについて混乱し、次のコマンドラインを編集するのが難しくなります。に先頭の改行を追加しない限り$PS1、従来のプログラムに続いて刺激になります。
トビー・スペイト

17

高度に投票された回答は、後続の改行が優先されるべき優れた技術的理由をすでに示しているので、別の角度からアプローチします。

私の意見では、以下はプログラムをより読みやすくします:

  1. 高い信号対雑音比(単純だが単純ではない)
  2. 重要なアイデアが最初に来る

上記の点から、後続の改行の方が優れていると言えます。改行はメッセージと比較すると「ノイズ」をフォーマットしているため、メッセージが目立つため、最初に来る必要があります(構文の強調表示も役立ちます)。


19
はい、"ok\n"はるかに優れてい"\nok"ます
...-cmaster

@cmaster:CのPascal文字列APIを使用してMacOSについて読んだことを思い出します。これは、すべての文字列リテラルの前に、などのマジックエスケープコードを付ける必要がありました"\pFoobar"
-grawity

16

末尾の改行を使用すると、後の変更が簡単になります。

OPのコードに基づく(非常に簡単な)例として、「初期化」メッセージのに出力を生成する必要があり、その出力は異なるソースファイルのコードの異なる論理部分から来ると仮定します。

最初のテストを実行し、「Initializing」が他の出力の行の最後に追加された場合、コードを検索して出力先を見つけ、「Initializing」を「\ nInitializingさまざまな状況で、他の形式を台無しにしません。

ここで、新しい出力が実際にオプションであるという事実を処理する方法を検討します。したがって、「\ n初期化」への変更により、出力の先頭に不要な空白行が生成されることがあります...

前の出力があるかどうかを示すグローバル(ショックホラー?? !!!)フラグを設定して、オプションの先頭の「\ n」で「Initializing」を印刷するかどうかをテストしますか、または「\ n」を出力しますか以前の出力で、他のすべての出力メッセージのように、この「初期化」先頭に「\ n」を持たない理由を疑問に思う将来のコードリーダーを残しますか?

終了する必要がある行の終わりに到達したことがわかった時点で、後続の改行を一貫して出力する場合、これらの問題をすべて回避します。行ごとに出力するいくつかのロジックの最後に別のputs( "\ n")ステートメントが必要になる場合がありますが、ポイントは、必要なことがわかっているコードの最も早い場所で改行を出力することです。他の場所ではなくそれをしてください。


1
すべての独立した出力項目が独自の行に表示されることになっている場合、後続の新しい行は正常に機能します。ただし、実際に複数のアイテムを統合する必要がある場合、事態はさらに複雑になります。共通ルーチンを介してすべての出力をフィードすることが実用的である場合、最後の文字がCRの場合は行末までを挿入する操作、最後の文字が改行の場合は何も、最後の文字の場合は改行を挿入する操作プログラムが独立した行のシーケンスを出力する以外の何かをする必要がある場合、それは何か他のものでした。
supercat

7

なぜprintfで先行するのではなく、末尾の改行を使用するのですか?

C仕様にほぼ一致します。

Cライブラリが定義されてラインを通り終わる改行文字 '\n'

テキストストリームは、に構成された文字の順序付けられたシーケンスであり、各行はゼロ以上の文字と終了改行文字で構成されます。最後の行に終了改行文字が必要かどうかは、実装で定義されます。C11§7.21.22

としてデータを書き込むコードは、ライブラリの概念に一致します。

printf("Initializing"); // Write part of a line
printf("\nProcessing"); // Finish prior line & write part of a line
printf("\nExiting");    // Finish prior line & write an implementation-defined last line

printf("Initializing\n");//Write a line 
printf("Processing\n");  //Write a line
printf("Exiting");       //Write an implementation-defined last line

Re:最後の行には、終了改行文字が必要です。常に'\n'出力にファイナルを作成し、入力にはそのファイナルを許容することをお勧めします。


スペルチェック

私のスペルチェッカーは文句を言います。おそらくあなたもそうです。

  v---------v Not a valid word
"\nProcessing"

 v--------v OK
"Processing\n");

私はispell.elそれによりよく対処するために一度改善しました。私はそれが\t問題であることがより頻繁であり、文字列を複数のトークンに分割することで回避できることを認めましたが、HTMLの非テキスト部分を選択的にスキップするより一般的な「無視」作業の副作用でしたまたはMIMEマルチパート本文、およびコードの非コメント部分。適切なメタデータ(<p lang="de_AT">またはContent-Language: gd)がある言語の切り替えに拡張するつもりでしたが、ラウンドトゥイットを取得することはありませんでした。そして、メンテナーは私のパッチを完全に拒否しました。:-(
トビースパイツ

@TobySpeightこれはラウンドtoitです。あなたの再改善を試してみてくださいispell.el
chux

4

多くの場合、先頭に改行があると、条件がある場合にコードを記述しやすくなります。たとえば、

printf("Initializing");
if (jobName != null)
    printf(": %s", jobName);
init();
printf("\nProcessing");

(ただし、他の場所で説明したように、CPU時間を多く消費するステップを実行する前に、出力バッファーをフラッシュする必要がある場合があります。)

したがって、両方の方法で良いケースを作ることができますが、個人的にはprintf()が好きではなく、出力を構築するためにカスタムクラスを使用します。


1
このバージョンが、後続の改行を含むバージョンよりも簡単に書ける理由を説明できますか?この例では、私には明らかではありません。代わりに、次の出力がと同じ行に追加されると問題が発生することがあります"\nProcessing"
ライムントクレーマー

ライムンドのように、私もこのような仕事から生じる問題を見ることができます。を呼び出すときは、周囲のプリントを考慮する必要がありprintfます。「初期化」行全体を条件付きにしたい場合はどうしますか?改行を前に付けるかどうかを知るには、その条件に「処理」行を含める必要があります。前に別の印刷があり、「Processing」行を条件付きにする必要がある場合、次の印刷をその条件に含めて、各印刷の前に別の改行を追加する必要があるかどうかを知る必要があります。
JoL

2
私は原則に同意しますが、この例は良いものではありません。より関連する例は、行ごとにいくつかのアイテムを出力することになっているコードです。出力が改行で終わるヘッダーで始まり、各行がヘッダーで始まることになっている場合、たとえばif ((addr & 0x0F)==0) printf("\n%08X:", addr);、使用するよりも無条件に出力に改行を追加する方が簡単な場合があります各行のヘッダーと末尾の改行に個別のコード。
-supercat

1

大手改行は、特に、他のライブラリ関数とうまく動作しないputs()perror標準ライブラリではなく、あなたが使用する可能性が高いです他のライブラリ。

事前に記述された行(定数、または既にフォーマットされた-などsprintf())を印刷する場合puts()は、自然な(そして効率的な)選択です。ただし、puts()前の行を終了して終了していない行を書き込む方法はありません。常に行末記号が書き込まれます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.